luaopenresty学习记录
lua数据类型
Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。
type()
没有赋值的变量也就为nil类型
> type(a)
nil
使用type
得到的数据类型作比较时应加上双引号
> type(a) == nil
false
> type(a) == "nil"
true
因为 type(type(X))==string
。
table
相当于 关联数组
,遍历表的方式:
a = {
['a'] = 1,
['b'] = 2
}
for k, v in pairs(a) do
print(k .. " : " .. v)
end
for k, v in ipairs(a) do
print(k .. " : " .. v)
end
pairs遍历table中的全部的key-vale,并非严格按照顺序
而ipairs会依据key的数值从1开始加1递增遍历相应的table[i]值
注意 Lua 中表的默认初始索引一般以 1 开始.
userdata(自定义类型)
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
从redis 中取数据时,若不存在所取的key,则返回值为 userdata
类型的null.
ngx.null
将lua 程序编译成二进制文件
lua本身可以使用luac将脚本编译为字节码
使用方法如下:
$ echo "print('hello')" > test.lua
$ luac -o test test.lua
$ lua test
hello
Lua 结合 nginx (openresty)
LuaNginxModule 的执行阶段说明
set_by_lua*: 流程分支处理判断变量初始化
rewrite_by_lua*: 转发、重定向、缓存等功能(例如特定请求代理到外网)
access_by_lua*: IP 准入、接口权限等情况集中处理(例如配合 iptable 完成简单防火墙) waf 通常部署阶段
content_by_lua*: 内容生成
header_filter_by_lua*: 响应头部过滤处理(例如添加头部信息)
body_filter_by_lua*: 响应体过滤处理(例如完成应答内容统一成大写)
log_by_lua*: 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其他机器)
nginx 配置
...
error_log /usr/local/openresty/nginx/logs/debug.log debug;
server {
listen 80;
server_name _;
index index.php index.html index.htm index.nginx-debian.html;
location / {
default_type 'text/html';
set_by_lua_file "set.lua";
content_by_lua_block {
ngx.say("hello,world")
}
access_by_lua_file "init.lua";
lua_code_cache off;
}
ngx_lua_module api
print() #与 ngx.print()方法有区别,print() 相当于ngx.log()
ngx.ctx #这是一个lua的table,用于保存ngx上下文的变量,在整个请求的生命周期内都有效,详细参考官方
ngx.location.capture() #发出一个子请求,详细用法参考官方文档。
ngx.location.capture_multi() #发出多个子请求,详细用法参考官方文档。
ngx.status #读或者写当前请求的相应状态. 必须在输出相应头之前被调用.
ngx.header.HEADER #访问或设置http header头信息,详细参考官方文档。
ngx.req.set_uri() #设置当前请求的URI,详细参考官方文档
ngx.set_uri_args(args) #根据args参数重新定义当前请求的URI参数.
ngx.req.get_uri_args() #返回一个LUA TABLE,包含当前请求的全部的URL参数
ngx.req.get_post_args() #返回一个LUA TABLE,包括所有当前请求的POST参数
ngx.req.get_headers() #返回一个包含当前请求头信息的lua table.
ngx.req.set_header() #设置当前请求头header某字段值.当前请求的子请求不会受到影响.
ngx.req.read_body() #在不阻塞ngnix其他事件的情况下同步读取客户端的body信息.[详细]
ngx.req.discard_body() #明确丢弃客户端请求的body
ngx.req.get_body_data() #以字符串的形式获得客户端的请求body内容
ngx.req.get_body_file() #当发送文件请求的时候,获得文件的名字
ngx.req.set_body_data() #设置客户端请求的BODY
ngx.req.set_body_file() #通过filename来指定当前请求的file data。
ngx.req.clear_header() #清求某个请求头
ngx.exec(uri,args) #执行内部跳转,根据uri和请求参数
ngx.redirect(uri, status) #执行301或者302的重定向。
ngx.send_headers() #发送指定的响应头
ngx.headers_sent #判断头部是否发送给客户端ngx.headers_sent=true
ngx.print(str) #发送给客户端的响应页面
ngx.say() #作用类似ngx.print,不过say方法输出后会换行
ngx.log(log.level,...) #写入nginx日志
ngx.flush() #将缓冲区内容输出到页面(刷新响应)
ngx.exit(http-status) #结束请求并输出状态码
ngx.eof() #明确指定关闭结束输出流
ngx.escape_uri() #URI编码(本函数对逗号,不编码,而php的urlencode会编码)
ngx.unescape_uri() #uri解码
ngx.encode_args(table) #将tabel解析成url参数
ngx.decode_args(uri) #将参数字符串编码为一个table
ngx.encode_base64(str) #BASE64编码
ngx.decode_base64(str) #BASE64解码
ngx.crc32_short(str) #字符串的crs32_short哈希
ngx.crc32_long(str) #字符串的crs32_long哈希
ngx.hmac_sha1(str) #字符串的hmac_sha1哈希
ngx.md5(str) #返回16进制MD5
ngx.md5_bin(str) #返回2进制MD5
ngx.today() #返回当前日期yyyy-mm-dd
ngx.time() #返回当前时间戳
ngx.now() #返回当前时间
ngx.update_time() #刷新后返回
ngx.localtime() #返回 yyyy-mm-dd hh:ii:ss
ngx.utctime() #返回yyyy-mm-dd hh:ii:ss格式的utc时间
ngx.cookie_time(sec) #返回用于COOKIE使用的时间
ngx.http_time(sec) #返回可用于http header使用的时间
ngx.parse_http_time(str) #解析HTTP头的时间
ngx.is_subrequest #是否子请求(值为 true or false)
ngx.re.match(subject,regex,options,ctx) #ngx正则表达式匹配,详细参考官网
ngx.re.gmatch(subject,regex,opt) #全局正则匹配
ngx.re.sub(sub,reg,opt) #匹配和替换(未知)
ngx.re.gsub() #未知
ngx.shared.DICT #ngx.shared.DICT是一个table 里面存储了所有的全局内存共享变量
ngx.shared.DICT.get
ngx.shared.DICT.get_stale
ngx.shared.DICT.set
ngx.shared.DICT.safe_set
ngx.shared.DICT.add
ngx.shared.DICT.safe_add
ngx.shared.DICT.replace
ngx.shared.DICT.delete
ngx.shared.DICT.incr
ngx.shared.DICT.flush_all
ngx.shared.DICT.flush_expired
ngx.shared.DICT.get_keys
ndk.set_var.DIRECTIVE
lua Require 问题
默认寻找路径:
/usr/local/openresty/lualib
可通过lua_package_path来指定:
lua_package_path '/foo/bar/?.lua;/blah/?.lua;;';
local config = require "waf.config"
local redis = require "resty.redis"
local cjson = require "cjson"
共享内存
ngx_lua 提供了一系列共享内存相关的 API (ngx.shared.DICT),可以很方便地通过设置过期时间来使得缓存被动过期,共享内存就是在内存块中分配出一个空间,让几个不相干的进程都能访问存储在这里面的变量数据。
当开辟的共享内存存满了之后,若继续插入,会使用强制删除(LRU算法)进行插入
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
http {
#lua_shared_dict <name> <size>
lua_shared_dict ip_blacklist 10m;
server {
.....
content_by_lua_block{
local ip_blacklist = ngx.shared.ip_blacklist
local ok, err = ip_blacklist:set("192.168.2.2", 1)
if ok then
ngx.say("ok")';
end
ngx.say(ip_blacklist:get("192.168.2.2"))
local ok, err = ip_blacklist:expire("192.168.2.2", 10)
...
}
}
}
lua+Redis
local config = require "waf.config"
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(2000)
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.say("failed to connect: ", err)
red:close()
end
red:get
red:set
red:expire
red:SMEMBERS
red:auth
...
例子:ip动态封禁
ngx.var.VARIABLE
读或者写nginx的变量
location /foo {
set $my_var '';
content_by_lua_block {
ngx.var.my_var = 123
...
}
}
仅仅是已经定义了的 Nginx 变量可以被写入
无法动态为nginx创建变量
设置 ngx.var.my_var 为 nil 值将会删除 $my_var Nginx 变量:
local config = require "waf.config"
local redis = require "resty.redis"
local cjson = require "cjson"
local red = redis:new()
red:set_timeouts(2000) -- 2 sec
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.say("failed to connect: ", err)
red:close()
end
res, err = red:get("pass_proxy")
if tostring(res) == "userdata: NULL" then
red:set("pass_proxy","false")
elseif red:get("pass_proxy") == "true" then
local dynamic_target = ngx.req.get_post_args()["dynamic_target"]
red:set("proxy_addr",dynamic_target)
end
....
local add = red:get("proxy_addr")
ngx.var.proxy_addr = add
location / {
default_type 'text/html';
set $proxy_addr '';
proxy_pass http://$proxy_addr/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
access_by_lua_file "init.lua";
lua_code_cache off;
}
Lua Waf 老版本通用绕过 (lua-nginx-module < v0.10.13)
Nginx Lua获取参数时,默认获取前100个参数值,其余的将被丢弃。