Openresty: Nginx核心加很多第三方模块组成,默认集成lua开发环境,提供了大量组件如Mysql、Redis、Memcached等等。
安装
(1)步骤
#!/bin/bash
tar xvf openresty-1.9.7.3.tar.gz
cd openresty-1.9.7.3
./configure --with-luajit --with-http_drizzle_module --with-http_iconv_module --with-ld-opt="-Wl,-rpath,/usr/local/lib" --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module
gmake
gmake install
--with-luajit 集成luajit模块
(2)安装结果
Luajit:luajit环境,lua是一种解释语言,通过luajit可以即时编译lua代码到机器代码,得到很好的性能。
Lualib:要使用的lua库,里边提供了一些默认的lua库,如redis,json库等,也可以把一些自己开发的或第三方库放在这。
Config:存放nginx配置文件。
(3)配置环境
a.配置配置文件路径
在http{}块中添加如下内容:
include /usr/local/openresty/config/*.conf;
所有的配置文件存放在/usr/local/openresty/config/目录下。
b.另外,如果在测试环境中,为了在修改完lua代码之后立即生效,不需要执行nginx -s reload,可以在http{}块中添加lua_code_cache off;默认开启,表示缓存lua代码。
3、nginx+lua开发
(1)流程:
a).接收请求
nginx配置文件
在/usr/local/openresty/config/目录下创建xxx.conf文件,名字随意。
hello.conf
server {
listen 80;
server_name xxx.xxx.xxx.xxx;
access_log /var/log/hello_access_80.log main;
location /hello {
default_type "text/html";
#接收到请求后,hello.lua做处理
content_by_lua_file /usr/local/openresty/lua/hello.lua;
}
}
对外提供的接口为http://xxx.xxx.xxx.xxx/hello
b).处理请求
hello.lua
local uri_args = ngx.req.get_uri_args()
for k, v in pairs(uri_args) do
ngx.say(k, ": ", v, "
")
end
ngx.say("hello")
表示获取请求参数。以k:v方式输出。
c).获取响应
浏览器:http://xxx.xxx.xxx.xxx/hello?instance=100¶ms=test
params: test
instance: 100
hello
(2)常用API
1、基础API
a).ngx.var: nginx变量
案例:
nginx配置文件:
location /var {
default_type "text/html";
set $a 1;
set $b 2;
content_by_lua_file /data/local/openresty/lua/var.lua;
}
lua文件:
local var = ngx.var
ngx.say("ngx.var.a: "..var.a.."
")
ngx.say("ngx.var.b: "..var.b.."
")
ngx.say("ngx.var.uri: "..var.uri.."
")
ngx.say("ngx.var.remoteip: ".. ngx.var.remote_addr)
ngx.var.uri获取location后的值。
ngx.var.remote_addr获取客户端ip。
b).ngx.req.get_method():获取请求类型
获取POST,GET等请求类型。
c).ngx.req.get_uri_args():获取url请求参数
适用于get请求的请求参数。参考上面的案例。
d).ngx.req.get_post_args():获取post请求内容体
在之前需要调用ngx.req.read_body()读取body体。
案例:
ngx.req.read_body()
local uri_args = ngx.req.get_post_args()
ngx.say(uri_args.method)
但采用postman发送请求,该方法无法获取到postman的参数,适用于表单以及js发送ajax请求。
e).ngx.req.get_body_data():获取post请求参数
cjson = require("cjson")
ngx.req.read_body()
local uri_args = ngx.req.get_body_data()
uri_args = cjson.decode(uri_args)
ngx.say(uri_args.method)
若通过postman发送post请求,获取到的参数是未解析的请求body体内容字符串,采用该方法获取参数。
f).ngx.say()、ngx.print():输出响应
ngx.say("hello") --表示输出hello/r/n
ngx.print("hello") --表示输出hello
3、nginx+lua全局内存
nginx是一个Master进程多个Worker进程的工作方式,因此我们可能需要在多个Worker进程中共享数据,那么此时就可以使用ngx.shared.DICT来实现全局内存共享。
(1)在nginx.conf文件中分配内存大小
lua_shared_dict shared_data 10m
(2)nginx路由配置
location /dict {
default_type "text/html";
content_by_lua_file /usr/local/openresty/lua/dict.lua;
}
(2)lua代码
a).案例1:
--获取共享内存变量
local shared_data = ngx.shared.shared_data
--获取字典值
local port = shared_data:get("port")
if not port then
ngx.say("port is null ")
--设置字典值
shared_data:set("port", "3306")
end
--重新获取字典值
local port_new = shared_data:get("port")
ngx.say("port_new: "..port_new)
b).案例2:
local shared_data = ngx.shared.shared_data
--删除字典
shared_data:delete("port")
--获取字典
local port = shared_data:get("port")
if not port then
ngx.say("port is null
")
end
4、nginx+lua访问控制模块
nginx共11个处理阶段,而相应的处理阶段是可以做插入式处理,即可插拔式架构;另外指令可以在http、server、server if、location、location if几个范围进行配置:
(1)init_by_lua、init_by_lua_file
使用范围:http
阶段: loading-config
当 nginx master 进程在加载 nginx 配置文件时运行指定的 lua 脚本,通常用来注册 lua 的全局变量或在服务器启动时预加载 lua 模块。
案例1:
a).nginx.conf(在http{}块中)
init_by_lua 'cjson = require "cjson"';
b).配置文件
location /init {
default_type "text/html";
content_by_lua 'ngx.say(cjson.encode({dog = 5, cat = 6}))';
}
案例2:
a).nginx.conf(在http{}块中)
init_by_lua '
local shared_data = ngx.shared.shared_data;
shared_data:set("test",50)
';
b).配置文件
location /test {
default_type "text/html";
content_by_lua '
local shared_data = ngx.shared.shared_data;
ngx.say(shared_data:get("test"))
';
}
不要在这个阶段初始化你的私有lua全局变量,因为使用lua全局变量会照成性能损失,并且可能导致全局命名空间被污染。
(2)init_worker_by_lua、init_worker_by_lua_file
使用范围:http
阶段:starting-worker
这个hook通常用来创建每个工作进程的计时器(通过lua的ngx.timer API),进行后端健康检查或者其它日常工作。
(3)set_by_lua、set_by_lua_file
使用范围:server、server if、location、location if
阶段:rewrite
设置一个变量,常用与计算一个逻辑,然后返回结果 该阶段不能运行Output API、Control API、Subrequest API、Cosocket API。
Output API: (ngx.say和ngx.send_headers)
Control API: (ngx.exit)
Subrequest API: (ngx.location.capture和ngx.location.capture_multi)
Cosocket API: (ngx.sleep)
这个指令是为了执行短期、快速运行的代码因为运行过程中nginx的事件处理循环是处于阻塞状态的。耗费时间的代码应该被避免。
案例:
a).配置文件
location /set {
default_type "text/html";
set $diff '';
set_by_lua_file $num /usr/local/openresty/lua/set.lua;
echo "sum = $num, diff = $diff";
}
b).lua代码
local a = 32
local b = 56
ngx.var.diff = a - b;
return a + b;
set_by_lua_file运行Nginx外部的lua脚本,能够避免在配置文件里使用大量的转义。
(4)rewrite_by_lua、rewrite_by_lua_file
实现url重写。在rewrite阶段运行。
a).配置文件
location /rewrite {
default_type "text/html";
rewrite_by_lua_file /data/local/openresty/lua/rewrite.lua;
echo 'in rewrite';
}
location =/other {
default_type "text/html";
echo 'in other';
}
b).lua代码
rewrite.lua代码
ngx.exec("/other")
此时发送请求http://xxx.xxx.xxx.xxx/rewrite,由于location被rewrite改写了,所以请求结果为in other。
(5)access_by_lua、access_by_lua_file
执行在access阶段。用于訪问控制。
a).配置文件
location /access {
default_type "text/html";
access_by_lua_file /data/local/openresty/lua/access.lua;
echo "welcome";
}
b).lua代码
if ngx.var.arg_user == "ntes" then
return
else
ngx.exit(ngx.HTTP_FORBIDDEN)
end
当访问http://xxx.xxx.xxx.xxx/access?user=ntes,输出welcome
当访问http://xxx.xxx.xxx.xxx/access?user=test,输出403Forbidden
(6)content_by_lua、content_by_lua_file
在content阶段运行,生成http响应。因为content阶段仅仅能有一个handler。所以在与echo模块使用时,不能同一时候生效,我测试,添加echo后,只输出echo后的结果。
查看上面的案例。
(7)ngx.location.capture
用于发出一个同步的,非堵塞的Nginxsubrequest(子请求)。
a).配置文件
location =/other {
default_type "text/html";
echo 'in other';
}
location /capture {
default_type "text/html";
content_by_lua_file /data/local/openresty/lua/capture.lua;
}
b).lua代码
local res = ngx.location.capture("/other")
if res.status == 200 then
ngx.print(res.body)
end
(8)ngx.location.capture_multi
并行的、非堵塞的发出多个子请求
a).配置文件
location /son {
default_type "text/html";
echo "in son";
}
location /daughter {
default_type "text/html";
echo "in daughter";
}
location /multi {
default_type "text/html";
content_by_lua_file /data/local/openresty/lua/multi.lua;
}
b).lua代码
local res1,res2 = ngx.location.capture_multi({{"/son"}, {"/daughter"}})
if res1.status == 200 then
ngx.say(res1.body)
end
if res2.status == 200 then
ngx.say(res2.body)
end
ngx.location.capture_multi并行的、非堵塞的发出多个子请求。这种方法在全部子请求处理完毕后返回。而且整个方法的执行时间取决于执行时间最长的子请求,并非全部子请求的执行时间之和。
在Lua代码中的网络IO操作仅仅能通过Nginx Lua API完毕。假设通过标准Lua API会导致Nginx的事件循环被堵塞,这样性能会急剧下降。在进行数据量相当小的磁盘IO时能够採用标准Lua io库,可是当读写大文件时这样是不行的,由于会堵塞整个NginxWorker进程。为了获得更大的性能。强烈建议将全部的网络IO和磁盘IO托付给Nginx子请求完毕(通过ngx.location.capture)。
5、nginx+lua web网页开发(模板渲染)
(1)下载template.lua
进入/usr/local/openresty/lualib/resty目录,下载template.lua
wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template.lua
(2)配置模板位置
在server{}中添加
set $template_location "/template";
set $template_root "/usr/local/openresty/template/";
(3)nginx配置
location /template {
default_type "text/html";
content_by_lua_file /usr/local/openresty/lua/template.lua;
}
(4)lua代码(template.lua)
local template = require "resty.template"
local context = {
title = "测试",
name = "张三",
description = "成绩统计",
age = 20,
hobby = {"电影", "音乐", "阅读"},
score = {语文 = 90, 数学 = 80, 英语 = 70},
score2 = {
{name = "语文", score = 90},
{name = "数学", score = 80},
{name = "英语", score = 70},
}
}
template.render("template.html", context)
(7)html代码(template.html)
{# 不转义变量输出 #}
姓名:{* string.upper(name) *}
{# 转义变量输出 #}
简介:{{description}}
{# 可以做一些运算 #}
年龄: {* age + 1 *}
{# 循环输出 #}
爱好:
{% for i, v in ipairs(hobby) do %}
{* v *}
{% end %}
成绩:
{% local i = 1; %}
{% for k, v in pairs(score) do %}
{* k *} = {* v *}
{% i = i + 1 %}
{% end %}
成绩2:
{% for i = 1, #score2 do local t = score2[i] %}
{* t.name *} = {* t.score *}
{% end %}
{# 中间内容不解析 #}
(8)访问http://xxx.xxx.xxx.xxx/template
可以随意改变lua中的值去渲染页面。
PS
下方是我个人订阅号,会一直更新各类技术文章,欢迎关注 :)