1. 为什么值得入手?
Nginx 作为现在使用最广泛的高性能后端服务器,Openresty 为之提供了动态预言的灵活,当性能与灵活走在了一起,无疑对于被之前陷于臃肿架构,苦于提升性能的工程师来说是重大的利好消息,本文就是在这种背景下,将初入这一未知的领域之后的一些经验与大家分享一下,若有失言之处,欢迎指教。
2. 安装
现在除了能在 [Download](http://openresty.org/en/download.html )里面下载源码来自己编译安装,现在连预编译好的[包](http://openresty.org/en/linux-packages.html)都有了, 安装也就分分钟的事了。
3. hello world
/path/to/nginx.conf`, `conftent_by_lua_file 里面的路径请根据 lua_package_path 调整一下。
“`
location / {
content_by_lua_file ../luablib/hello_world.lua;
}
“`
`/path/to/openresty/lualib/hello_world.lua`
“`
ngx.say(“Hello World”)
“`
访问一下, Hello World~.
“`
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: application/octet-stream
Date: Wed, 11 Jan 2017 07:52:15 GMT
Server: openresty/1.11.2.2
Transfer-Encoding: chunked
Hello World
“`
基本上早期的 Openresty 相关的开发的路数也就大抵如此了, 将 lua 库发布到 lualib 之下,将对应的 nginx 的配置文件发布到 nginx/conf 底下,然后 reload 已有的 Openresty 进程(少数需要清空 Openresty shared_dict 数据的情况需要重启 )。
如果是测试环境的话,那更是简单了,在 http 段将 lua_code_cache 设为 off , Openresty 不会缓存 lua 脚本,每次执行都会去磁盘上读取 lua 脚本文件的内容,发布之后就可以直接看效果了(当然如果配置文件修改了,reload 是免不了了)。是不是找到一点当初 apache 写 php 的感觉呢:)
4. 开发语言 Lua 的大致介绍
环境搭建完毕之后,接下来就是各种试错了。关于 Lua 的介绍,网上的资料比如:Openresty 最佳实践(版本比较多,这里就不放了)写的都会比较详细,本文就不在这里过多解释了,只展示部分基础的Lua的模样。
下面对 lua 一些个性有趣的地方做一下分享,可能不会涉及到 lua 语言比较全面或者细节的一些部分,作为补充,读者可以翻阅官方的<<Programing in Lua>>。
“`lua
— 单行注释以两个连字符开头
–[[
多行注释
–]]
— 变量赋值
num = 13 — 所有的数字都是双精度浮点型。
s = ‘单引号字符串’
t = “也可以用双引号”
u = [[ 多行的字符串
]]
— 控制流程,和python最明显的差别可能就是冒号变成了do, 最后还得数end的对应
— while
while n < 10 do
n = n + 1 — 不支持 ++ 或 += 运算符。
end
— for
for i = 0, 9 do
print(i)
end
— if语句:
f n == 0 then
print(“no hits”)
elseif n == 1 then
print(“one hit”)
else
print(n .. ” hits”)
end
–只有nil和false为假; 0和 ”均为真!
if not aBoolValue then print(‘false’) end
— 循环的另一种结构:
repeat
print(‘the way of the future’)
num = num – 1
until num == 0
— 函数定义:
function add(x, y)
return x + y
end
— table 用作键值对
t = {key1 = ‘value1’, key2 = false}
print(t.key1) — 打印 ‘value1’.
— 使用任何非nil的值作为key:
u = {[‘@!#’] = ‘qbert’, [{}] = 1729, [6.28] = ‘tau’}
print(u[6.28]) — 打印 “tau”
— table用作列表、数组
v = {‘value1’, ‘value2’, 1.21, ‘gigawatts’}
for i = 1, #v do — #v 是列表的大小
print(v[i])
end
— 元表
f1 = {a = 1, b = 2} — 表示一个分数 a/b.
f2 = {a = 2, b = 3}
— 这会失败:
— s = f1 + f2
metafraction = {}
function metafraction.__add(f1, f2)
local sum = {}
sum.b = f1.b * f2.b
sum.a = f1.a * f2.b + f2.a * f1.b
return sum
end
setmetatable(f1, metafraction)
setmetatable(f2, metafraction)
s = f1 + f2 — 调用在f1的元表上的__add(f1, f2) 方法
— __index、__add等的值,被称为元方法。
— 这里是一个table元方法的清单:
— __add(a, b) for a + b
— __sub(a, b) for a – b
— __mul(a, b) for a * b
— __div(a, b) for a / b
— __mod(a, b) for a % b
— __pow(a, b) for a ^ b
— __unm(a) for -a
— __concat(a, b) for a .. b
— __len(a) for #a
— __eq(a, b) for a == b
— __lt(a, b) for a < b
— __le(a, b) for a <= b
— __index(a, b) <fn or a table> for a.b
— __newindex(a, b, c) for a.b = c
— __call(a, …) for a(…)
“`
以上参考了
[learn lua in y minute](https://learnxinyminutes.com/docs/zh-cn/lua-cn/ ) ,做了适当的裁剪来做说明。
4.1 Lua 语言个性的一面
4.1.1 第一道墙: 打印 table
作为 lua 里面唯一标准的数据结构, 直接打印居然只有一个 id 状的东西,这里说这一点没有抱怨的意思,只是让读者做好倒腾的心理准备,毕竟倒腾一个简洁语言终归是有代价的。
了解决定背后的原因,有时候比现成的一步到位的现成方案这也是倒腾的另一面好处吧,这里给出社区里面的[讨论](http://luausers.org/wiki/TableSerialization)
举个例子:
lua 里面一般使用 #table 来获取 table 的长度,究其原因,lua 对于未定义的变量、table 的键,总是返回 nil,而不像 python 里面肯定是抛出异常, 所以 # 来计算 table 长度的时候只会遍历到第一个值为 nil 的地方,毕竟他不能一直尝试下去,这时候就需要使用 table.maxn 的方式来获取了。
4.1.2 Good or Bad ? 自动类型转换
如果你在 python 里面去把一个字符串和数字相加,python 必定以异常回应。
“`python
>>> “a” + 1
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
TypeError: cannot concatenate ‘str’ and ‘int’ objects
“`
但是 Lua 觉得他能搞定。
“`lua
> = “20” + 10
30
“`
如果你觉得 Lua 选择转换加号操作符的操作数成数字类型去进行求值显得不可思议的,下面这种情况下,这种转换又貌似是可以有点用的了 print(“hello” .. 123) ,这时你不用手动去将所有参数手工转换成字符串类型。
尚没有定论说这项特性就是一无是处,但是这种依赖语言本身不明显的特性的代码笔者是不希望在项目里面去踩雷的。
4.1.3 多返回值