nginx|lua

编辑nginx.conf配置文件

vim /usr/local/nginx/conf/nginx.conf

在http部分添加配置

#lua模块路径,多个之间用";"分隔,其中";;"表示默认搜索路径,默认到 /usr/local/nginx 下找包路径
lua_package_path "/usr/local/lualib/?.lua;;";  #lua模块
lua_package_cpath "/usr/local/lualib/?.so;;";   # c模块
include lua.conf;  #在http部分添加lua配置文件

编辑lua.conf文件

在 /usr/local/nginx/conf 目录下创建一个lua.conf
vim /usr/local/nginx/conf/lua.conf

#lua.conf
server{
    listen    80;
    server_name  _; 
}
# 将代码写在配置文件里面的简易版,这有一个问题,代码多了之后,不容易维护
location /lua{   # 添加访问接口,接口名字是 /lua  返回helloword
    # http://127.0.0.1/lua    返回 hello  world
    default_type "text/html";
    content_by_lua "ngx.say('hello world')";
}
# 升级版 location
location /lua{
    default_type "text/html";
    content_by_lua_file conf/lua/test.lua;   # 相对于nginx 安装目录
    # 这里也可以使用绝对路径  /usr/local/nginx/conf/lua/test.lua
    lua_code_cache off;
    # lua_code_cache 默认是开启的,即缓存lua代码,即每次lua代码变更必须reload nginx才生效
    # 如果在开发阶段可以通过lua_code_cache off; 关闭缓存,这样调试时每次修改lua代码不需要reload nginx
    # 但是正式环境一定记得开启缓存
}
# 然后编辑test.lua 文件
vim /usr/local/nginx/conf/lua/test.lua
# 添加以下内容
ngx.say("hello world");

测试nginx是否正常

/usr/local/nginx/sbin/nginx -t
显示如下内容说明配置成功
nginx: the configuration file /usr/servers/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/servers/nginx/conf/nginx.conf test is successful

重启nginx

/usr/local/nginx/sbin/nginx -s reload

nginx|lua API

ngx.req.discard_body()

忽略请求体,如果不需要post传送的参数,就在lua文件首行加这句话,如果需要body内容就要不能加这句话,
要加ngx.req.read_body(),下面有获取post参数的nginx|lua API

ngx.req.get_headers([max_headers]) 获取请求头

获取请求的请求头信息。返回结果是一个table。
local headers, err = ngx.req.get_headers()
ngx.say("host: ", headers["Host"])
当遇到一个key对应多个值的时候,比如
Foo: foo
Foo: bar
Foo: baz
会返回一个table
ngx.req.get_headers()["Foo"] 会得到 {"foo", "bar", "baz"}

ngx.req.get_headers() 有一个可选参数max_headers来控制能够最大处理一个key对应的多少个值
比如:
ngx.req.get_headers(10) 上面返回的table中最多有10个元素,多余的参数会被默认丢弃
ngx.req.get_headers(0) 移除最大限制。

在获取headers里面的内容时,会默认全部转换为小写取请求参数

ngx.req.get_uri_args([max_args]) 获取get请求的参数

获取uri里面的参数,返回结果是一个table

不一样的uri获取到的参数类型不一样,比如以下这些请求。

GET /test?foo=bar&bar=baz&bar=blah  一键多值类型
结果 foo : bar (字符串)  bar :  {"baz","blah"} (table类型)

GET /test?a%20b=1%61+2    非转义字符类型
结果 a b: 1a 2   非转义的字符会自动转义过来

GET /test?foo&bar
结果  foo:true    bar:true    布尔类型

GET /test?foo=&bar=
结果  foo:    bar:       空字符串类型

GET /test?=hello&=world   空的key类型,这种参数将自动丢弃
返回一个空的输出 return an empty output for instances

ngx.req.read_body() 获取POST请求的参数

ngx.req.read_body()
local args = ngx.req.get_post_args()

注: 
如果使用 ngx.req.get_post_args()  得到的将是一个lua  table
如果使用 ngx.req.get_body_data() 得到的将是一个lua string, rather then a lua table

If the request body is already read previously by turning on lua_need_request_body or by using other modules, then this function does not run and returns immediately.
如果请求体已经被lua_need_request_body或者其他模块抢先解析了,
则ngx.req.read_body()不会执行,并且立即返回

If the request body has already been explicitly discarded, either by the ngx.req.discard_body function or other modules, this function does not run and returns immediately.
如果请求体通过ngx.req.discard_body()明确放弃解析,那么ngx.req.read_body()
也不会执行,并且立即返回

ngx.req.http_version() 请求http协议版本

Returns the HTTP version number for the current request as a Lua number

ngx.req.get_method() 获取请求的请求方法

Retrieves the current request's request method name. Strings like `"GET"` and `"POST"` are returned instead of numerical method constants.

If the current request is an Nginx subrequest, then the subrequest's method name will be returned.

ngx.req.raw_header([true]) 获取原始的请求头内容

Returns the original raw HTTP protocol header received by the Nginx server.

ngx.print(ngx.req.raw_header())
GET /t HTTP/1.1
Host: localhost
Connection: close
Foo: bar

You can specify the optional no_request_line argument as a true value to exclude the request line from the result. For example,

ngx.print(ngx.req.raw_header(true))
Host: localhost
Connection: close
Foo: bar

输出响应

ngx.header 输出响应头
ngx.print 输出相应内容体
ngx.say 同ngx.print 但是最后输出一个换行符
ngx.exit 指定状态码退出

example.conf 配置文件
location /lua_response_1{
      default_type  "text/html";
      content_by_lua_file  /usr/example/lua/test_response_1.lua
}

test_response_1.lua文件
--写响应头
ngx.header.a = "1"
--多个响应头可以使用table
ngx.header.b = {"2", "3"}
--输出响应
ngx.say("a","b","<br/>")
ngx.print("c","d","<br/>")
--200状态码推出
return ngx.exit(200)

ngx.redirect 重定向

ngx.redirect("http://jd.com",302)
ngx.status=状态码,设置响应的状态码  ngx.resp.get_headers()或者设置的响应状态码
ngx.send_headers()发送响应状态码,当调用ngx.say/ngx.print时自动发送响应状态码;
可以通过ngx.headers_sent=true 判断是否发送了响应状态码

nginx其他API

ngx.escape_uri/ngx.unescape_uri   :  uri 编码解码
ngx.encode_args/ngx.decode_args  :  参数编码解码
ngx.encode_base64/ngx.decode_base64  :  BASE64编码解码
ngx.re.match   :nginx正则表达式匹配

比如
--未经解码的请求uri
local request_uri = ngx.var.request_uri;
ngx.say("request_uri : ", request_uri, "<br/>");
--解码
ngx.say("decode request_uri : ", ngx.unescape_uri(request_uri), "<br/>");
--MD5
ngx.say("ngx.md5 : ", ngx.md5("123"), "<br/>")
--http time
ngx.say("ngx.http_time : ", ngx.http_time(ngx.time()), "<br/>")

nginx全局内存

nginx是一个master进程多个worker进程的工作方式,因此我们可能需要在多个worker进程中共享数据。
那么此时就使用ngx.shared.DICT来实现全局内存共享
1.首先在nginx.conf的http部分分配内存大小

#共享全局变量,在所有worker间共享
lua_shared_dict  shared_data 1m;

2.example.conf配置文件

location /lua_shared_dict{
    default_type  "text/html";
    content_by_lua_file  /usr/example/lua/test_lua_shared_dict.lua;
}

3.test_lua_shared_dict.lua

-- 1.获取全局共享内存变量
local shared_data = ngx.shared.shared_data
-- 2.获取字典值
local i = shared_data:get("i")
if not i then
    i = 1
   -- 3.惰性赋值
    shared_data:set("i",i)
    ngx.say("lazy set i", i, "<br/>")
end
-- 递增
i = shared_data:incr("i",1)
ngx.say("i=",i,"<br/>")

ngxin lua 模块指令

init_by_lua 和 init_by_lua_file

所处的处理阶段 loading-config
使用的范围 http

每次nginx重新加载配置时执行,可以用它来完成一些耗时模块的加载,或者初始化一些全局配置;
在Master进程创建worker进程时,此指令中加载的全局变量会进行copy-onwrite,即会复制到所有全局变量到worker进程中

  1. nginx.conf配置文件中的http部分添加
# 共享全局变量,在所有worker间共享
lua_shared_dict  shared_data 1m;
init_by_lua_file  /usr/example/lua/init.lua
  1. init.lua
-- 初始化耗时的模块
local redis = require  'resty.redis'
local cjson = require  'cjson'

-- 全局变量,不推荐
count = 1

-- 共享全局内存
local shared_data = ngx.shared.shared_data
shared_data : set("count",1)
  1. test.lua
count = count + 1
ngx.say("global variable:",count)
local shared_data = ngx.shared.shared_data
ngx.say(", shared memory:", shared_data:get("count"))
shared_data:incr("count",1)
ngx.say("hello,world")

4.访问http://localhost/lua 会发现全局变量一直不变,而共享内存一直递增

global variable : 2, shared memory : 8 hello world

注意:一定在生产环境开启lua_code_cache,否则每个请求都会创建lua VM实例

init_worker_by_lua 和init_worker_by_lua_file

所处的处理阶段 starting-worker
使用的范围 http
通常用于定时拉取配置/数据,或者后端服务的健康检查

用于启动一些定时任务,比如心跳检查,定时拉取服务器配置
此处的任务是跟worker进程数量有关系的,比如有2个worker进程
那么就会启动两个完全一样的定时任务

  1. nginx.conf配置文件中的http部分添加
init_worker_by_lua_file  /usr/example/lua/init_worker.lua;

2.init_worker.lua

local count = 0
local delayInSeccounds = 3
local heartbeatCheck = nil

heartbeatCheck = function(args)
    count = count + 1
    ngx.log(ngx.ERR, "do check", count)
    local ok , err = ngx.timer.at(delayInSeconds,heartbeatCheck) 
    if not ok then
        ngx.log(ngx.ERR,"failed to startup heartbeart worker ...", err)
    end
end

heartbeatCheck()

另外根据实际情况设置如下指令

lua_max_pending_timers 1024;  # 最大等待任务数
lua_max_running_timers 256;  # 最大同时运行任务数

set_by_lua 或 set_by_lua_file

设置nginx变量
语法 set_by_lua  变量名  'lua_code';
set_by_lua_file  变量名  file_path;

local uri_args =ngx.req.get_uri_args()  -- 获取url 参数

rewrite_by_lua 或 rewrite_by_lua_file

语法

rewrite_by_lua  "lua_code"
rewrite_by_lua_file  file_path

可能会用到的语法

ngx.redirect("url", status)  重定向到url ,状态码status

ngx.req.set_uri(uri,false)   内部重写uri,但是false参数决定不重新发起请求
ngx.req.set_uri(uri,true)  内部重写uri,true参数决定会重新发起请求
ngx.req.set_uri_args({a=1,b=2}) 重写请求的uri参数

access_by_lua 和 access_by_lua_file 用于访问控制

比如我们只允许内网ip访问,可以使用以下形式

allow 127.0.0.1;
allow 192.168.0.0/16;
deny all;

有可能用到的其他技术

ngx.exit(status)   -- 立即中断当前http请求,后续lua代码将不会再执行,status代表状态码 。不影响使用了proxy_pass的子请求。

content_by_lua 和 content_by_lua_file

用于location中, 给每个请求执行一段lua代码

ngx.sub(str,start,stop)

截取字符串,范围是从start到 stop

ngx.utctime

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容