OpenResty简介及学习笔记
- 摘要
- 简介
- 一、OpenResty综述
- 二、指令说明:
- *_by_lua
- *_by_lua_block {lua_script}
- *_by_lua_file
- 三、登陆验证
- IP防火墙
- 操作头信息检测
- 登陆缓存
- 过滤参数
- 四、输出过滤
- header_filter_by_lua*
- body_filter_by_lua*
- 五、Redis
摘要
OpenResty系列文章之一。
前些日子做的一些研究,期间收获了一些对web后台应用的理解,nginx运行机理的部分理解。
本篇包括简介、部分模块的介绍。
简介
OpenResty (也称为 ngx_openresty)是一个全功能的 Web 应用服务器。它打包了标准的 Nginx 核心,很多的常用的第三方模块,以及它们的大多数依赖项。
通过众多进行良好设计的 Nginx 模块,OpenResty 有效地把 Nginx 服务器转变为一个强大的 Web 应用服务器,基于它开发人员可以使用Lua 编程语言对 Nginx 核心以及现有的各种Nginx C 模块进行脚本编程,构建出可以处理一万以上并发请求的极端高性能的 Web 应用。
OpenResty 致力于将你的服务器端应用完全运行于 Nginx 服务器中,充分利用 Nginx 的事件模型来进行非阻塞 I/O 通信。不仅仅是和 HTTP 客户端间的网络通信是非阻塞的,与MySQL、PostgreSQL、Memcached、以及 Redis 等众多远方后端之间的网络通信也是非阻塞的。
因为 OpenResty 软件包的维护者也是其中打包的许多 Nginx 模块的作者,所以 OpenResty 可以确保所包含的所有组件可以可靠地协同工作。
作者简介:
agentzh,本名章亦春,现任 CloudFare 系统工程师,主要是 Nginx 和 OpenResty 开发,是一名快乐的程序员,现定居美国旧金山。曾经在北京的时候供职于 Yahoo!中国以及淘宝(阿里巴巴)。
教程:agentzh 的 Nginx 教程
其他内容可以参看: OpenResty简介,OpenResty 作者章亦春访谈实录
一、OpenResty综述
OpenResty®通过 Lua 扩展 NGINX 实现的可伸缩的 Web 平台
先扔三个网站:
首先,OpenResty官网!
还有Github:OpenResty
为数不多的书籍:OpenResty最佳实践(Lua语言入门也可以全靠它)
下图给出的是Lua Nginx Module中各指令的执行顺序
- set_by_lua: 流程分支处理判断变量初始化
- rewrite_by_lua: 转发、重定向、缓存等功能(例如特定请求代理到外网)
- access_by_lua: IP 准入、接口权限等情况集中处理(例如配合 iptable 完成简单防火墙)
- content_by_lua: 内容生成
- header_filter_by_lua: 应答 HTTP 过滤处理(例如添加头部信息)
- body_filter_by_lua: 应答 BODY 过滤处理(例如完成应答内容统一成大写)
- log_by_lua: 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其他机器)
实际上我们只使用其中一个阶段 content_by_lua,也可以完成所有的处理。但这样做,会让我们的代码比较臃肿,越到后期越发难以维护。把我们的逻辑放在不同阶段,分工明确,代码独立,后期发力可以有很多有意思的玩法。
二、指令说明:
*_by_lua < lua-script-str >
无后缀的指令,后加字符串型的lua程序。
如:
location / {
default_type text/html;
content_by_lua '
ngx.say("<p>hello, world</p>")
';
}
nginx
*_by_lua_block {lua_script}
有block后缀,可以直接跟lua程序段。
如:
location / {
content_by_lua_block{
local test = 'Hello,world'
ngx.say(test)
}
}
*_by_lua_file < path-to-lua-script-file >
file后缀跟lua文件路径
如:
location ~ ^/api/([-_a-zA-Z0-9/]+) {
# 准入阶段完成参数验证
access_by_lua_file lua/access_check.lua;
#内容生成阶段
content_by_lua_file lua/$1.lua;
}
nginx
三、登陆验证
在access_by_lua*
中集中进行一些权限认证,防止恶意ip、非法行为进入到服务器中。
location / {
access_by_lua_block{
ngx.exit(ngx.HTTP_FORBIDDEN)
}
content_by_lua_block{
--内容生产阶段
}
}
IP防火墙
OpenResty最佳实践提到:禁止某些终端访问
上面文档下方推荐第三方包:Github,Lua-resty-iputils
操作头信息检测
通过access_by_lua*
可以对请求进行过滤,比如可以在这里检查有没有带authorization
头部信息,若没有带可以用ngx.exit()
直接退出。
登陆缓存
Github: lua-resty-jwt模块进行jwt校验
结合lua-resty-jwt
和lua-resty-redis
甚至可以将服务器登陆缓存移植到nginx上。
过滤参数
还未尝试。
防止 SQL 注入
四、输出过滤
通常是header_filter_by_lua*
与body_filter_by_lua*
配合实现。
【文档来源官方】:
注意下列API函数现在还不能在set_by_lua*
、header_filter_by_lua*
、body_filter_by_lua*
、log_by_lua*
四个环境中使用:
- 输出API 函数 (e.g., ngx.say and ngx.send_headers)
- 控制API 函数 (e.g., ngx.redirect and ngx.exec)
- 子请求API 函数 (e.g., ngx.location.capture and ngx.location.capture_multi)
- Cosocket API 函数 (e.g., ngx.socket.tcp and ngx.req.socket).
header_filter_by_lua*
使用Lua代码在lua块中定义输出头信息。
example1:
location / {
proxy_pass http://mybackend;
header_filter_by_lua 'ngx.header.Foo = "blah"';
}
nginx
example2:
header_filter_by_lua_block {
ngx.header["content-length"] = nil
}
body_filter_by_lua*
参考:
谈谈 OpenResty 中的 body_filter_by_lua*
输入数据块chunks
要经过ngx.arg1 (Lua 字符串)的过滤,"eof"标志指示响应体数据流的末端通过ngx.arg2 (Lua bollean值)显示。
这个情景下,"eof"标志就是主请求的last_buf
或子请求的 last_in_chain
。"eof" == "true" 表示此次nginx请求的响应结束了。
由于响应体可能会分多块发送,body_filter_by_lua*
可能会被多次调用。详细机理参考上面文献有提到。
location /t {
echo hello world;
echo hiya globe;
body_filter_by_lua '
......
';
}
nginx
比如上面,body_filter_by_lua*
首次调用时ngx.arg1 的值只是hello world
,不包括下面的hiya globe
。
我验证了一下:
location = /test_bf {
echo hello world;
echo this;
echo is;
echo world;
header_filter_by_lua_block {
ngx.header.content_length = nil
}
body_filter_by_lua_block {
if string.match(ngx.arg[1], "this") then
ngx.arg[2] = true
ngx.arg[1] = "end"
return
end
}
}
上述location发送四块数据,匹配到this便设置"eof"不再发送下去了。
$ curl
结果:
$ hello world
$ end
要点:
① ngx.arg[2] = true
设置新的"eof"标志使截断响应,设置"eof"依旧是有效的响应。
② ngx.arg[1] = "end"
修改ngx.arg[1]
的值,即为修改输出响应。而当需要替换一些信息,仅需一行代码即可实现。
ngx.arg[1] = ngx.re.gsub(ngx.arg[1], "o", "*")
上文也提到。上述即为将所有 ' o ' 字符替换为 ' * ' 字符
若该location
作为其他location
的子请求,而作为子请求不想被过滤数据。需要通过ngx.is_subrequest
判断。
body_filter_by_lua_block {
if ngx.arg[1] and not ngx.is_subrequest then
......
end
}
nginx
还有一点,如果在body_filter_by_lua*
中的Lua代码会修改响应体的长度,这就需要去把Content-Length
的响应头去除掉。我之前测试与前端联调时,就是发现body过滤必须要字符数相同才会显示,很头疼不知道什么原因。其实只要去掉这个头就行了。
location = / {
proxy_pass ......
header_filter_by_lua_block {
ngx.header.content_length = nil
}
body_filter_by_lua_block {
ngx.arg[1] = ngx.re.gsub(ngx.arg[1], "o", "**")
}
}
五、Redis
官方github:lua-resty-redis
OpenResty最佳实践:访问有授权验证的 Redis
官方包封装了一些Redis
的方法,使用起来还是挺舒适的。
本文链接:https://blog.maozhiting.com/post/openresty_notes.html
— EOF —
作者 毛毛
发表于 2017-11-05 15:11:55 ,并被添加「 OpenResty
」标签 ,最后修改于 2017-11-05 15:47:30