Lua学习笔记

Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。

1、概述

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

2、基础语法

2.1、注释

  • 单行注释:-
  • 多行注释:-[[ 注释的内容 ]]
  • 多行注释:–[====[ 注释和内容 ]====]

2.2、基础数据类型

  • nil 空(无效值)
  • boolean 布尔(true/false)
  • number 实数(类同 C/C++ 的 double 类型)
  • string 字符串
  • table 表(关联数组)
  • function 函数

2.3、表达式

算数运算符和关系运算符,与大多数语言一致,唯一特殊的是 不等于运算的写法为 ~=。

2.4、逻辑运算符

  • and 逻辑与
  • or 逻辑或
  • not 逻辑非

例如:

a and b 如果 a 为 nil或false,则返回 a,否则返回 b
a or b 如果 a 为 nil或false,则返回 b,否则返回 a

2.5、函数

1
2
3
function functionName(args..) 
....
end
  • 函数的定义一定要在函数调用前。
  • 函数的参数为按值传递,唯一例外的是表数据类型,是按址传递。
  • 函数参数支持变长参数。
  • 函数支持多个返回值。

2.6、类库

Lua提供了丰富的标准库(Lua在线手册),便于开发使用。

2.7、模块

从Lua5.1版本开始,就对模块和包添加了新的支持,可以使用require和module来定义和使用模块和包。

require用于使用模块,module用于创建模块。

简单的说,一个模块就是一个程序库,可以通过require来加载。然后便得到了一个全局变量,表示一个table。这个table就像是一个命名空间,其内容就是模块中导出的所有东西,比如函数和常量,一个符合规范的模块还应使require返回这个table。

例如:

complex.lua文件中创建模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
local _M = {}   -- 局部变量,模块名称

function _M.new(r, i)
return {r = r, i = i}
end

_M.i = _M.new(0, 1) -- 定义一个table型常量i

function _M.add(c1, c2) --复数加法
return _M.new(c1.r + c2.r, c1.i + c2.i)
end

function _M.sub(c1, c2) --复数减法
return _M.new(c1.r - c2.r, c1.i - c2.i)
end

return _M -- 返回模块的table

main.lua文件中使用模块。

1
2
3
4
5
6
7
local complex = require "complex"

local com1 = complex.new(0, 1)
local com2 = complex.new(1, 2)

local ans = complex.add(com1, com2)
print(ans.r, ans.i) -->output 1 3

2.8、变量

Lua 变量有三种类型:全局变量、局部变量、表中的域。

Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。

局部变量的作用域为从声明位置开始到所在语句块结束。

变量的默认值均为 nil。

1
2
3
4
5
6
7
8
9
10
11
12
13
a = 5               -- 全局变量
local b = 5 -- 局部变量

function joke()
c = 5 -- 全局变量
local d = 6 -- 局部变量
end

do
local a = 6 -- 局部变量
b = 6 -- 全局变量
print(a,b); --> 6 6
end

2.9、循环

数值for循环

1
2
3
for var=exp1,exp2,exp3 do  
<执行体>
end

var从exp1变化到exp2,每次变化以exp3为步长递增var,并执行一次”执行体”。exp3是可选的,如果不指定,默认为1。

1
2
3
for i=10,1,-1 do
print(i)
end

泛型for循环

1
2
3
4
for i,v in ipairs(a) 
do print(v)
end

i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。

while 循环

1
2
3
4
5
6
a=10
while( a < 20 )
do
print("a 的值为:", a)
a = a+1
end

3、Nginx中使用Lua

Lua文件中的代码是按照书写顺序执行的。如果在代码中调用了一个函数,但是这个函数在调用前没有定义,那么就会报错。

3.1、获取URL参数

1
2
3
4
5
6
7
8
location /test {
content_by_lua_block {
local arg = ngx.req.get_uri_args()
for k,v in pairs(arg) do
ngx.say("[GET ] key:", k, " v:", v)
end
}
}

3.2、获取Body内容

1
2
3
4
5
6
7
8
9
10
location /test {
content_by_lua_block {
ngx.req.read_body()
local arg = ngx.req.get_post_args()
for k,v in pairs(arg) do
ngx.say("[POST] key:", k, " v:", v)
end
}
}

或者

1
2
3
4
5
6
7
8
9
location /test {
set body_data "";
content_by_lua_block {
ngx.req.read_body()
local data = ngx.req.get_body_data()
ngx.var.body_data = data --Lua变量赋值给Nginx变量
ngx.say(data)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#设置纯lua的外部模块搜索路径(';;' is the default path):

lua_package_path '/foo/bar/?.lua;/blah/?.lua;;';

#设置使用c编写的lua外部模块搜索路径(can also use ';;'):
lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;';

server {
location /inline_concat {
default_type 'text/plain';

set $a "hello";
set $b "world";
# 嵌入lua脚本
set_by_lua $res "return ngx.arg[1]..ngx.arg[2]" $a $b;
echo $res;
}

location /rel_file_concat {
set $a "foo";
set $b "bar";
# 脚本路径是nginx安装路径的相对路径
# $ngx_prefix/conf/concat.lua 内容是:
#
# return ngx.arg[1]..ngx.arg[2]
#
set_by_lua_file $res conf/concat.lua $a $b;
echo $res;
}

location /abs_file_concat {
set $a "fee";
set $b "baz";
# 使用绝对路径
set_by_lua_file $res /usr/nginx/conf/concat.lua $a $b;
echo $res;
}

location /lua_content {
default_type 'text/plain';

content_by_lua "ngx.say('Hello,world!')";
}

location /nginx_var {
default_type 'text/plain';

# 尝试请求 /nginx_var?a=hello,world 看看效果
content_by_lua "ngx.print(ngx.var['arg_a'], '\\n')";
}

location /request_body {
# 读取request body (default off)
lua_need_request_body on;
client_max_body_size 50k;
client_body_buffer_size 50k;

content_by_lua 'ngx.print(ngx.var.request_body)';
}

# lua通过子请求来实现透明非阻塞的IO
location /lua {
default_type 'text/plain';

content_by_lua '
local res = ngx.location.capture("/some_other_location")
if res.status == 200 then
ngx.print(res.body)
end';
}

# 发送一个GET /recur?num=5的请求,查看效果
location /recur {
default_type 'text/plain';

content_by_lua '
local num = tonumber(ngx.var.arg_num) or 0

if num > 50 then
ngx.say("num too big")
return
end

ngx.say("num is: ", num)

if num > 0 then
res = ngx.location.capture("/recur?num=" .. tostring(num - 1))
ngx.print("status=", res.status, " ")
ngx.print("body=", res.body)
else
ngx.say("end")
end
';
}

location /foo {
rewrite_by_lua '
res = ngx.location.capture("/memc",
{ args = { cmd = "incr", key = ngx.var.uri } }
)
';

proxy_pass http://blah.blah.com;
}

location /blah {
access_by_lua '
local res = ngx.location.capture("/auth")

if res.status == ngx.HTTP_OK then
return
end

if res.status == ngx.HTTP_FORBIDDEN then
ngx.exit(res.status)
end

ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
';

# proxy_pass/fastcgi_pass/postgres_pass/...
}
#调用lua脚本文件
location /mixed {
rewrite_by_lua_file /path/to/rewrite.lua;
access_by_lua_file /path/to/access.lua;
content_by_lua_file /path/to/content.lua;
}

# 在代码中使用nginx var
# WARN: nginx var的内容必须仔细的过滤,
# 否则会有很大的安全风险!
location ~ ^/app/(.+) {
content_by_lua_file /path/to/lua/app/root/$1.lua;
}

location / {
lua_need_request_body on;

client_max_body_size 100k;
client_body_buffer_size 100k;

access_by_lua '
-- 查看客户端ip是否在我们的黑名单中
if ngx.var.remote_addr == "132.5.72.3" then
ngx.exit(ngx.HTTP_FORBIDDEN)
end

-- 查看请求body中是否有不和谐的词语
if ngx.var.request_body and
string.match(ngx.var.request_body, "fsck")
then
return ngx.redirect("/terms_of_use.html")
end

-- tests passed
';

# proxy_pass/fastcgi_pass/etc settings
}
}

error_log说明:

1
2
2017/08/10 03:27:40 [error] 39644#39644: *9859091 open() "/etc/nginx/html/favicon.ico" failed (2: No such file or directory), client: 113.107.44.46, server: user.kiswo.com, request: "HEAD /favicon.ico HTTP/1.0", host: "user.kiswo.com"

日志格式:YYYY/MM/DD HH:MM:SS [LEVEL] PID#TID: *CID MESSAGE

每列的含义:

  • 进程id
  • 线程id
  • CID是一个识别(可能代理的)连接的数字,可能是一个计数器。CID部分是可选的。
  • client:
  • server:
  • request:
  • upstream:
  • host:
  • referrer:
文章作者: OneRain
文章链接: https://kiswo.com/2016/09/03/tools/nginx/lua-note/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 OneRain's Blog