天下武功,为快不破。Nginx 的看家本领就是速度,Lua 的拿手好戏亦是速度,这两者的结合在速度上无疑有基因上的优势。最先将 Nginx,Lua 组合到一起的是 OpenResty,它有一个 ngx_lua 模块,将 Lua 嵌入到了 Nginx 里面。本教程从环境搭建到实战讲解,逐步向读者展示如何使用 Nginx+Lua 框架进行开发。
在 Lua 中混合处理不同 Nginx 模块输出(proxy, drizzle, postgres, Redis, memcached 等)。 在请求真正到达上游服务之前,Lua 中处理复杂的准入控制和安全检查。 比较随意的控制应答头(通过 Lua)。 从外部存储中获取后端信息,并用这些信息来实时选择哪一个后端来完成业务访问。 在内容 handler 中随意编写复杂的 web 应用,同步编写异步访问后端数据库和其他存储。 在 rewrite 阶段,通过 Lua 完成非常复杂的处理。 在 Nginx 子查询、location 调用中,通过 Lua 实现高级缓存机制。 对外暴露强劲的 Lua 语言,允许使用各种 Nginx 模块,自由拼合没有任何限制。该模块的脚本有充分的灵活性,同时提供的性能水平与本地 C 语言程序无论是在 CPU 时间方面以及内存占用差距非常小。所有这些都要求 LuaJIT 2.x 是启用的。其他脚本语言实现通常很难满足这一性能水平。
这里官网并没有给出答案,我根据我们的应用场景给大家列举,并简单描述一下原因: 有长时间阻塞调用的过程 例如通过 Lua 完成系统命令行调用 使用阻塞的Lua API完成相应操作 单个请求处理逻辑复杂,尤其是需要和请求方多次交互的长连接场景 Nginx的内存池 pool 是每次新申请内存存放数据 所有的内存释放都是在请求退出的时候统一释放 如果单个请求处理过于复杂,将会有过多内存无法及时释放 内存占用高的处理 受制于Lua VM的最大使用内存 2G 的限制 这个限制是单个Lua VM,也就是单个Nginx worker 两个请求之间有交流的场景 例如你做个在线聊天,要完成两个用户之间信息的传递 当前OpenResty还不具备这个通讯能力(后面可能会有所完善) 与行业专用的组件对接 最好是 TCP 协议对接,不要是 API 方式对接,防止里面有阻塞 TCP 处理 由于OpenResty必须要使用非阻塞 API ,所以传统的阻塞 API ,我们是没法直接使用的 获取 TCP 协议,使用 cosocket 重写(重写后的效率还是很赞的) 每请求开启的 light thread 过多的场景 虽然已经是light thread,但它对系统资源的占用相对是比较大的 这些适合、不适合信息可能在后面随着 OpenResty 的发展都会有新的变化,大家拭目以待。
docker pull openresty/openresty
docker run -d --name openresty -p 8000:80 -v /Users/zhangguofu/app/docker/openresty/conf.d:/etc/nginx/conf.d:Z -v /Users/zhangguofu/app/docker/openresty/data:/data openresty/openresty
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
lua_package_cpath "/usr/local/openresty/lualib/?.so;;";
# 包含lua配置文件
include /etc/nginx/conf.d/lua.conf;
server {
listen 80;
server_name _;
# 代理到lua脚本
location /lua {
default_type 'text/html';
content_by_lua_file /etc/nginx/conf.d/conf/lua/test.lua;
}
}
# 在文件中写入迁入的代码
ngx.say("hello world");
# 重启 docker
docker restart openresty
# 查看 日志
docker logs -f openresty
ngx.header['Content-Type']="text/html; charset=utf-8";
local info=ngx.req.get_uri_args()["id"];
ngx.say(info);
return;
# 重启容器
zhangguofu@zhangguofudeMBP lua $ docker restart openresty
openresty
#查看输出日志
zhangguofu@zhangguofudeMBP lua $ docker logs -f openresty
2021/01/12 06:46:01 [warn] 1#1: conflicting server name "_" on 0.0.0.0:80, ignored
nginx: [warn] conflicting server name "_" on 0.0.0.0:80, ignored
172.17.0.1 - - [12/Jan/2021:06:46:18 +0000] "GET / HTTP/1.1" 200 1097 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"
2021/01/12 06:46:18 [error] 6#6: *2 open() "/usr/local/openresty/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: _, request: "GET /favicon.ico HTTP/1.1", host: "127.0.0.1:8000", referrer: "http://127.0.0.1:8000/"
172.17.0.1 - - [12/Jan/2021:06:46:18 +0000] "GET /favicon.ico HTTP/1.1" 404 561 "http://127.0.0.1:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"
172.17.0.1 - - [12/Jan/2021:06:46:25 +0000] "GET /lua HTTP/1.1" 200 22 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"
2021/01/12 06:51:41 [warn] 1#1: conflicting server name "_" on 0.0.0.0:80, ignored
zhangguofu@localhost bin $ ./redis-cli -h 192.168.9.195 -p 16379 --raw
192.168.9.195:16379>
192.168.9.195:16379>
192.168.9.195:16379> keys *
name
192.168.9.195:16379>
192.168.9.195:16379> set auds 200
OK
192.168.9.195:16379> get auds
200
ngx.header['Content-Type']="text/html; charset=utf-8";
local info=ngx.req.get_uri_args()["id"];
local redis = require "resty.redis"
local red = redis:new()
local ok, err = red:connect("192.168.9.195", 16379)
if not ok then
ngx.say("failed to connect: ", err)
return
end
ok, err = red:incr(info)
if not ok then
ngx.say("failed to set dog: ", err)
return
end
local views=red:get(info)
ngx.say("get result: ", views)
192.168.9.195:16379> get auds
205
ngx.header['Content-Type']="text/html; charset=utf-8";
local info=ngx.req.get_uri_args()["id"];
local redis = require "resty.redis"
local red = redis:new()
-- 引入json 包
local cjson = require "cjson.safe";
local ok, err = red:connect("192.168.9.195", 16379)
if not ok then
ngx.say("failed to connect: ", err)
return
end
ok, err = red:incr(info)
if not ok then
ngx.say("failed to set dog: ", err)
return
end
local views=red:get(info)
data_end1={}
data_end1['code']=200;
data_end1['views']=views;
ngx.say(cjson.encode(data_end1))
这里面用到了lua语言,其实语言对于程序员来说,语言就是一条路而已,通过这条路到达我们的目的地,即实现我们的功能,但是条条大路通罗马,语言很多,能够精通一两个语言,甚至为某个语言开发几个包文件,我觉得就很了不起了。但是我们要不断拓宽自己的知识面,为什么?一个功能,实习生能实现,你也能实现,,甚至一个小白谷歌一下也能写出来,但是差距在哪里?你用了更高效的方式,更贴合需求的方案。