1.概述
openRestry
location /lua {
default_type text/html;
content_by_lua 'ngx.say("User-Agent:", ngx.req.get_headers()["User-Agent"])';
}
Openresty的主要组成
- Nginx
- Nginx官方模块
- Openresty工具
- Openresty第三方模块
Openresty第三方模块
- lua语言模块
- ngx_http_lua_module
- ngx_stream_lua_module
运行机制
nginx框架
- 事件驱动系统
- HTTP框架
- STREAM框架
nginx模块
- ngx_http_lua_module
- ngx_stream_lua_module
- 其他第三方模块
ngx_http_lua_module
- 纯Lua代码
- 与Nginx交互的SDK
- 配合 nginx.conf 和 lua指令使用
Openresty中的SDK
- cosocket通讯: udp,tcp
- 基于共享内存的字典shared.DICT
- 定时器
- 基于协程的并发编程
- 获取客户端请求与响应的信息.
- 修改客户端请求与响应,包括发送响应
- 子请求
工具类
- 正则表达式
- 日志
- 系统配置
- 编解码
- 时间
Openresty的使用要点
- 不破坏Nginx的事件驱动体系,不使用任何会阻塞Nginx进程进行事件调度的方法。
- 不调用会导致Nginx进程主动休眠的方法
- 谨慎调用第三方库,若不是通过cosocket实现(例如Lua标准网络库或者第三方服务提供的SDK库),就无法融入Nginx的事件驱动体系中,那么对TCP消息的处理通常会导致Nginx进程进入Sleep状态
- 不调用会长时间占用CPU的方法避免Lua代码块执行密集计算指令
- 不破坏Nginx的低内存消耗优点,避免对每个请求分配过大内存。 对会导致分配内存的Lua SDK,谨慎分配内存。考虑用状态机多次分批处理
- 理解Lua代码块如何嵌入Nginx中执行
- 理解Lua SDK详细用法,及它们如何集成在Nginx中处理请求
- 保持Lua代码的高效
2.OpenResty中的Nginx模块与 Lua模块
Openresty的Nginx模块: 核心模块
ndk_http_module # Openresty的基础模块,全名为Nginx Development Kit,顾名思议是一个开发工具包,为如ngx_http_lua_module等模块提供通用功能 ,通过-without-ngx_devel_kit_module禁用模块.
ngx_http_lua_module #Openresty提供HTTP服务lua编程能力的核心模块,在所有阶段、过滤、负载均衡等允许用lua语言处理请求通过--without-http lua module禁用模块
ngx_http_lua_upstream_module # 作为ngx http lua modulc模块的补充,为lua编程提供更多的关于upstream管理的API通过--without-http_lua upstream_module禁用模块
ngx_stream_lua_module # Openresty提供四层TCP/UDP服务lua编程能力的核心模块,允许用lua语言处理请求通过--without-stream lua module禁用模块
Openresty的Nginx模块: 反向代理模块
ngx_http_memc_module # content阶段的反向代理模块,上游为Memcached,相比Nginx官方的memcached模块 (仅提供GET命令),它提供了全部的命令
ngx_http_redis2_module # content阶段的反向代理模块,上游为Redis服务。
ngx_http_redis_module # content阶段的反向代理模块,上游为Redis服务。这两个redis模块皆为Nginx模块,使用nginx.conf语法,皆可被Lua语言版本的lua-resty-redis取代(它使用ngx_http_lua_module的tcp sdk)。 通过--without-http redis module禁用模块
ngx_http_postgres_module # content阶段的反向代理模块,上游为Postgres数据库 ,通过--with-http posteres module启用该模块
ngx_http_drizzle_module # content阶段的反向代理模块,上游为Mvsql或者Drizzle数据库 通过--with-http drizzle module启用该模块
Openresty的Nginx模块: 工具模块
ngx_http_headers_more_filter_module #rewvrite阶段处理请求、过滤模块,修改请求、响应的HTTP头部
ngx_http_rds_json_filter_module # 过滤模块,将RDS格式响应转换为JSON格式
ngx_http_xss_filter_module #使Nginx支持aiax跨站请求
ngx_http_rds_csv_filter_module #过滤模块,将RDS格式响应转换为CSV格式
ngx_http_srcache_filter_module # 在access阶段、响应过滤时生效,针对location在内存中缓存响应.
ngx_coolkit_module #提供一些小功能集合,例如从Basic HTTP Authentication协议中取出密码作为变量值,等等
ngx_http_iconv_module #可以将变量或者HITTP响应从一种编码(例如GBK) 转换为另一种编码 (如UTF8)
ngx_http_echo_module #在content阶段、响应过滤时皆会生效,生成或者修改返回客户端的响应
ngx_http_set_misc_module #扩展了ngx http rewrite module模块的set指令
ngx_http_encrypted_session_module #使用AES256算法对变量的值进行加密和解密.
ngx_http_form_input_module #rewrite阶段读取请求包体,将POST和PUT请求中的FORM表单内容,解析成nginx变量供其他模块使用.
ngx_http_array_var_module #以固定分隔符的形式取出生成数组变量,并可将数组变量再通过固定分隔符邹将变量中数组类型的值,生成新的字符串变量
例子
server {
listen 8341;
location /sharedict {
content_by_lua_block {
local dogs = ngx.shared.dogs
dogs:set("Jim", 8,0.1,3)
ngx.sleep(1)
local jim,flags,stale = dogs:get stale("Jim")
local bill,bflags = dogs:get('bill')
success,err,forcible = dogs:add('bill',123)
require "resty.core.shdict"
local capacity_bytes = dogs:capacity()
print(success,":",err,":",forcible,":",dogs:free_space(),"/",capacity bytes)
ngx.say(jim,',',flags,',',stale,':',bill,",",bflags)
}
}
location /array {
array_split, $arg_names to=$names;
array join '+' $names;
#return 200"$namesin";
content_by_lua_block {
gx.say(ngx.var.names)
}
}
}
curl localhost:8341/array?names=l,2,3
# 输出 1+2+3
Openresty的官方Lua模块
lua-redis-parser # 将原始的redis响应解析为Lua语言的数据结构
lua-rds-parser # 将原始的Drizzle、Mysql、PostGres数据库响应解析为Lua语言的数据结构
lua-resty-dns # 基于cosocket实现DNS协议的通讯
lua-resty-memcached # 基于ngxsockettcp实现的memcached客户端
lua-resty-redis #基于ngx.socket.tcp实现的redis客户端
lua-resty-mysql #基于ngx.socket.tcp实现的mysql客户端
lua-resty-upload。 # 实现了读取请求中上传文件内容的功能
lua-resty-upstream-healthcheck #通过向上游服务发送心跳检查上游服务的健康状态,以踢除有问题的节点
lua-resty-string # 提供了hash函数 (如SHA1、MD5等)、字符串转换函数等工具
lua-cjson #提供Lua语言与Json数据结构之间的编码、解码工具库
lua-resty-websocket #基于ngx_http_lua_module实现了websocket协议的客户端及服务器端
lua-resty-limit-traffic #基于Lua语言实现了多种限速方案
Lua库lua-resty-lock #提供基于共享内存的非阻塞互斥锁,基于ngx_http_lua_module中的ngxsleep方法实现"
lua-resty-lrucache #基于内存实现的LRU (Least Recently Used) 缓存,故不能跨Nginx worker进程使用.通过
lua-resty-core #ngx_http_lua_module模块提供的SDK皆基于老的CAPI实现,而本模块使用方式重新实现了ngx_http_lua_module模块中的SDK
3.如何在Nginx中嵌入Lua代码
init_by_lua/init_by_lua_block/init_by_lua_file # 在Nginx解析配置文件 (Master进程)时在Lua VM层面立即调用的Lua代码
init_worker_by_lua/init_worker_by_lua_block/init_worker_by_lua_file #在Nginx worker进程启动时调用,实际实现了ngx module t中的init process方法
在11个HTTP阶段中嵌入Lua代码
- set_by_lua/set_by_lua_block/set_by_lua_file
- rewrite_by_lua/rewrite_by_lua_block/rewrite_by_lua_file
- access_by_lua/access_by_lua_block/access_by_lua_file
- content_by_lua/content_by_lua_block/content_by_lua_file
- log_by_lua/log_by_lua_block/log_by_lua_file
控制rewrite/access是否延迟执行Lua代码
rewrite_by_lua_no_postpone # 控制是否在rewrite阶段延迟至最后再执行Lua代码,默认关闭
access_by_lua_no_postpone # 控制是否在access阶段延迟至最后再执行Lua代码,默认关闭
在过滤响应、负载均衡时嵌入Lua代码
- header_filter_by_lua/header_filter_by_lua_block/header_filter_by_lua_file
- body_filter_by_lua/body_filter_by_lua_block/body_filter_by_lua_file
- balancer_by_lua_block/balancer_by_lua_file
在Openssl处理SSL协议时嵌入Lua代码
- ssl_certificate_by_lua_block/ssl_certificate_by_lua_file
- ssl_session_fetch_by_lua_block/ssl_session_fetch_by_lua_file
- ssl_session_store_by_lua_block/ssl_session_store_by_lua_file
在Lua代码中获取当前阶段
ngx_get_phase
4.OpenResty中Lua与C代码交互的原理
提供一种Lua语言调用C语言函数的功能
- ffi.cdef声明C语言中的函数或者结构体.
- ffi.C调用声明说的方法
- tonumber为Lua语言标准库函数
- _M为Lua的数据结构:表
- #s是Lua语法,表示取得字符串s长度
- ngxatoi是Nginx中的工具函数,用于将
- 字符串转换为数字
例子
lua-resty-string: string.lua
local ffi = require "ffi"
local C = ffi.C
local tonumber = tonumber
local _M = { _VERSION = '0.11' }
ffi.cdef[[
intptr_t ngx_atoi(const unsigned char *line, size_t n);
]]
function _M.atoi(s)
return tonumber(C.ngx_atoi(s, #s))
end
return _M
typedef intptr_t ngx_int_t;
#define u_char unsigned char
ngx_int_t ngx_atoi(u_char *line, size_t n) …}
系统级配置指令
lua_malloc_trim # 每N个请求使用C库的malloc trim方法,将缓存的空闲内存归还操作系统。该功能是在11个阶段中的log阶段判断是否需要执行的。值为0时表示关闭该功能
lua_code_cache # 值为on表示LuaVM由所有请求共享,值为of表示每个请求使用独立的Lua VM(这也导致)
lua_package_path # 设置调用Lua模块的路径地址
lua_package_cpath # 设置Lua调用C模块的路径地址
取得配置参数SDK
ngx.config.subsystem # 获取Nginx子模块,例如http分下会返回http,而stream升下会返回stream
ngx.config.debug # 以布尔值返回当前Nginx编译执行configure时是否加入--with-debug
ngx.config.prefix # 返回Nginx执行时的prefix路径,由命令行的-p或者configure--prefix指定的路径
ngx.config.nginx_version # 以整型返回Neinx版本,例如1.13.6会返回1013006
ngx.config.nginx_configure # 返回编译时执行configure命令的参数
ngx.config.ngx_lua_version # 以整型返回ngx_http_lua_module版本号,例如10013 (ngx http lua version宏)
5.获取、修改请求与响应的SDK
## 读取、修改变量的SDK
ngx.var.VAR_NAME
# 根据变量名VAR NAME读取Nginx中的变量值 (包括set定义的变量,以及Nginx框架生成的变量,例如http_HEADER或者arg _NAME)。也可修改变量的值,但必须是已经存在的变量。
## 客户端提前关闭连接
lua_check_client_abort #是否检查客户端关闭连接,且发现该事件后回调ngx.on abort指定的Lua方法。默认关闭
ok, err = ngx.on_abort(callback) #当下游客户端过早关闭连接时,调用callback函数
## 获取请求头部的SDK
ngx.req.get_method # 以字符串形式返回当前请求的方法名,例如“GET
ngx.req.http_version #返回请求中的HTTP版本号,例如1.1、20等
ngx.req.get_uri_args # 以Lua table的方式返回当前请求的全部URI参数,若URI参数只有名没有值,则该值为true。最多返回100个参数
ngx.arg[index] # 按index索引号读取到用户请求中的参数
## 获取请求包体的SDK
lua_need_request_body # Ngimx在HTTP11个阶段中都不确保读取到完整的请求包体,而该指令可以强制要求在以上阶段Lua代码执行前读取完整的请求包体。默认关闭
ngx.req.get_post_args # 以Lua table的方式返回POST请求中的表单数据,最多返回100个成员
ngx.req.read_body # 以同步的方式读取当前请求的包体。当请求没有包体、包体已经被读取或者包体被丢弃时,该方法会立即返回。读取到的包体,需要通过get body data或者get body fle获取到内容
ngx.req.get_body_data # 以Lua字符串形式返回内存中的包体。当请求包体未被读取、请求包体被存放在磁盘文件中、请求包体不存在时,返nil
ngx.req.get_body_file # 返回磁盘中存放请求包体的文件名,当包体未被读取或者未被存放至文件时,返回nil
HTTP请求方法名常量
- ngx.HTTP_GET
- ngx.HTTP_HEAD
- ngx.HTTP_PUT
- ngx.HTTP_POST
- ngx.HTTP_DELETE
- ngx.HTTP_OPTIONS (added in the v0.5.0rc24 release)
- ngx.HTTP_MKCOL (added in the v0.8.2 release)
- ngx.HTTP_COPY (added in the v0.8.2 release)
- ngx.HTTP_MOVE (added in the v0.8.2 release)
- ngx.HTTP_PROPFIND (added in the v0.8.2 release)
- ngx.HTTP_PROPPATCH (added in the v0.8.2 release)
- ngx.HTTP_LOCK (added in the v0.8.2 release)
- ngx.HTTP_UNLOCK (added in the v0.8.2 release)
- ngx.HTTP_PATCH (added in the v0.8.2 release)
- ngx.HTTP_TRACE (added in the v0.8.2 release)
修改请求信息的SDK
ngx.req.set_method(method_id) # 以HTTP方法变量的方式设置当前请求的方法
ngx.req.set_header(name , value) # 修改或者删除当前请求的请求头部。当value为nil时,表示删除该头部
ngx.req.clear_header(header_name) #删除当前请求的某个请求头部,与ngx.reqset header中值为nil效果相同
ngx.req.set_uri_args(args) #设置当前请求的URI参数,可传入字符串(如ngx.req.set uri args(“a=3&b=hello%20world"))
修改请求包体的SDK
ngx.req.discard_body # 丢弃接收到的请求包体
ngx.req.set_body_data # 设置当前请求的请求包体内容。必须先读取完请求包体
ngx.req.set_body_file(filename, autoclean) # 以磁盘文件来设置请求中的包体。必须先读取完请求包体。若autoclean值为true,则请求执行完后会删除这个磁盘文件,该值默认为false
ngx.req.init_body(buffer_size) # 初始化一个空缓冲区,用于append body和finish body使用,创建用户请求包体。若buffer size没有传递则使用Nginx官方提供的client body buffer size值作为缓冲区大小。
ngx.req.append_body # 向缓冲区中写入请求包体内容。若添加的包体大小超出缓冲区,则将包体写入临时文件中
ngx.req.finish_body # 使用init body和append body添加完包体后,必须调用该方法
修改URI并跳转的SDK
ngx.req.is_internal # 以布尔值返回当前请求是否为内部跳转过来的请求
ngx.req.set_uri(uri, jump) # 修改当前请求的URI。jump默认为false,若为truc且在rewrite by lua上下文中时则类似rewritc指令进行location跳转
ngx.exec # 更改当前请求的URI并执行location内部跳转
HTTP响应状态码常量
value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release)
value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20
release)
value = ngx.HTTP_OK (200)
value = ngx.HTTP_CREATED (201)
读取、修改响应值
ngx.status
读取响应值,在响应头部发出以前可以修改响应值
修改发送响应的头部
ngx.header.HEADER成者ngx.header['HEADER']
# 增、删、改、查当前请求中的响应头部。注意lua transform underscores in response headers指令是默认开启中的。
# 其值为nil时(或者值为分) 表示删除该头部。当头部不存在时,则返回nil
lua_transform_underscores_in_response_headers
# 当我们通过SDK中的ngx.header系列API去设置、获取头部时,是否自动将头部名称 (不包括值)中的’更换为’’,默认是开启的
发送响应的SDK
ngx.redirect(uri, status?) # 向客户端返回重定向请求,status默认为302
ngx.send_headers # 显式的向客户端发送响应头部,返回1表示成功,返回nil表示失败
ngx.headers_sent # 返回布尔值标识响应头部是否已经发送
请求响应类指令
lua_use_default_type # 指定是否使用Nginx框架提供的default type中定义的Content-Type类型,作为Lua代码的响应类型。默认开启。
lua_http10_buffering # 。控制对于Lua生成的、HTTP/1.0协议的响应是否先缓存再发送,当Lua代码中显示设定了Content-Length头部时会自动关闭该缓存功能
发送响应的SDK
ngx.print # 通过写入响应包体向下游客户端发送响应。该方法是异步的,它会立刻返回而不等待所有响应发送完毕在其后使用
ngx.flush(true) # 可以同步等待发送成功
ngx.say # 与ngx.print相似,但在包体最后会加入换行符
ngx.flush(wait?) # 将响应发向客户端,wait参数默认为false,此时ush方法是异步的,即它返回时所有响应未必都已经传递给内核的发送缓冲区;当值为true时,flush方法是同步,它返回时要么响应发送完毕,要么超时
ngx.exit(status) # 当status>=200时,exit会终止当前请求的Lua代码,通过向Nginx返回响应码来发送响应当status--0时,exit会终止当前阶段(参见HTTP11个阶段)的执行,继续后续阶段的执行
ngx.cof # 显式的指定响应内容已经结束。对于HTTP/1.1 (RFC2616)中定义的chunked编码来说,eof会指定Nginx发出“last chunk”来指明响应结束
6.工具类型的SDK
Nginx函数返回值常量
- ngx.OK(0)
- ngx.ERROR(-1)
- ngx.AGAIN(-2)
- ngx.DONE(-4)
- ngx.DECLINED (-5)
取worker进程信息SDK
ngx.worker.exiting # 以布尔值返回当前Nginx worker进程是否正在退出
ngx.worker.pid # 返回worker进程ID,由于不依赖变量,所以比ngx.var.pid应用范围更广
ngx.worker.count # 返回nginx.conf中配置的Nginx worker进程的数量
ngx.worker.id # 返回所属worker进程的序号 (从0到N-1)
日志级别常量
- ngx.STDERR
- ngx.EMERG
- ngx.ALERT
- ngx.CRIT
- ngx.ERR
- ngx.WARN0
- ngx.NOTICE0
- ngx.INFO
- ngx.DEBUG
## 写入日志的SDK
print(...) # 使用notice级别将日志写入crror.log文件中
ngx.log(log level, ...) # 以指定的日志级别log level来写入日志至error.log文件中
# 获取error.log日志内容的SDK
lua_capture_error_log # 基于Nginx提供的ngx cycle t->ngx_log_intercept_pt机制,使用API中的get_logs (由lua-resty-core模块提供)直接在内存中获取到Nginx所有输出至error.log中的日志内容。该指令定义了缓存大小
get_logs
# local errlog = require "ngx.errlog“
# local res = errlog.get_logs(10)
# 建立上下文信息的SDK
ngx.ctx
# 针对每个HTTP请求在内存中建立上下文字典,可通过名称设置、读取值。子请求内部跳转后皆不能使用ctx上下文
# 编解码
ngx.escape_uri # 将字符串进行URL编码
ngx.unescape_uri # 将以URL编码的字符串解码
ngx.encode_args # 将Lua table以URL格式中参数的形式编码,如foo =3,“br”=“hello world”编码为foo=3&b%20r=hello%20world
ngx.decode_args # 将URL格式的参数解码为Lua table
ngx.encode_base64 # 将字符串编码为base64格式
ngx.decode_base64 # 将base64格式的字符串解码为原字符串
ngx.quote_sql_str # 将SQL语句字符串转换为Mysql格式
# 哈希编码SDK
ngx.crc32_short(str) # 计算字符串的CRC32编码,该方法适用于较短的字符串 (小于30~60字节)
ngx.crc32_long # 计算字符串的CRC32编码,该方法适用于较长的字符串 (大于30~60字节)
ngx.hmac_sha1(secret_key, str) # 返回字符串的SHA1摘要值 (依赖Openssl库)
ngx.md5(str) # 返回字符串的MD5摘要值
ngx.md5_bin # 返回字符串的二进制格式的MD5摘要值
ngx.sha1_bin # 返回字符串的二进制格式的SHA1摘要值
# 正则表达式SDK
ngx.re.match # 进行正则表达式匹配,返回匹配中的子字符串
ngx.re.find # 进行正则表达式匹配,返回匹配中的子字符串中的第1个、最后1个字符的索引。未创建新字符串
ngx.re.gmatch # 进行正则表达式匹配,但返回的是迭代器,需要通过迭代器取得所有匹配的子字符串
ngx.re.sub # 进行正则表达式匹配,并可将匹配中的子字符串替换为新的字符串
ngx.re.gsub # 进行正则表达式匹配,并以全局方式将匹配中的子字符串替换为新的字符串
lua_regex_match_limit # 限制ngx.re正则表达式类API匹配中的最大组数,默认值0表示使用PCRE库编译时设定的
最大值
lua_regex_cache_max_entries # 对于以下API: ngxrematch,ngx.regmatch,ngxresub,ngxre.gsub所生成的正则表达式缓存至内存中,该指令定义了缓存的最大条目数。当达到最大数目后,新的正则表达式不会被缓存。默认值1024
7.同步且非阻塞的底层SDK cosocket
lua_socket_connect_timeout # 设置SDK中的cosocket的连接超时时间,默认60秒,可以携带所有时间类单位
lua_socket_send_timeout # 设置SDK中的cosocket的两次写操作间的超时时间,默认60秒,可以携带所有时间类单位
lua_socket_read_timeout # 设置SDK中的cosocket的两次读操作间的超时时间,默认60秒,可以携带所有时间类单位
lua_socket_buffer_size # 设置cosocket中的读缓冲区大小,默认为4K/8K
lua_socket_pool_size # 设置每个sosocket连接池中的最大连接数 (每个worker进程),超出后使用LRU算法关闭、淘汰连接
lua_socket_keepalive_timeout # 设置每个cosocket连接的最大空闲时间 (类似HTTP的Keepalive),默认60秒
lua_socket_log_errors # 控制cosocket出错时是否记录错误日志至Nginx的error.log文件中
TCP cosocket
ngx.socket.tcp # 创建TCP协议的cosocket对象。当没有显示关闭cosocket或者把它放入连接池,则当前阶段结束时会自动关闭对象
connect # 连接上游服务(IP端口或者unix地址)
sslhandshake # 在已经建立好的TCP连接上进行SSL握手
send # 将消息发送到对端。在send方法返回时,它已经将用户态的字节流拷贝到内核socket缓冲区中
receive(size)或者receive(patlern?)
# 自cosocket中接收size个字节的消息,未接收足时则不会返回,直到满足超时条件(由settimeoul、setimeouts方法或者lua socket read timeout指令定义)
# pattern为*a表示一直接收消息,直到连接关闭
# pattemn为*1表示接收一整行消息,直到收到LF为止。没有参数时等同*1
receiveany(max) # 自cosocket中接收到任意数据即返回,最多接收max字节(若内核读缓冲区数据超过max,仍只返回数据
receiveuntil(pattern, options?) # 返回用于读取消息的Lua迭代器方法,直到patten匹配到接收到的字节流
close # 关闭TCP连接
settimeout(microseconds) # 设置超时时间,该时间同时应用在connect、send、receive方法上,参数单位为毫秒
settimeouts(connect timeout. send timeout read timeout)分别设置connect、send、receive方法的超时时间,单位毫秒.
setkeepalive(timeout?, size?) # 关闭cosocket对象,并将当前TCP连接放入keepalive连接池,最长空闲时间为timeout (单位毫秒),设为0表示永不讨期,若忽将该参数则使用lua socket keepalive timeout指令的值。
# size指定了连接池中对当前上游服务的最大连接数(仅第一次创建该上游服务的连接池时有效),忽略该参数时使用lua socket pool size指令的值达到size上限后(每worker进程内),按LRU关闭较老的空闲连接
# 若内核读缓冲区仍有数据,则该方法会返回错误信息“connection in dubious state"
getreusedtimes # 返回当前TCP连接复用了多少次
8.基于协程的并发编程SDK
协程SDK
co = ngx.thread.spawn(func, argl, arg2, ...)
# 生成轻量级线程并由nginx及ngx_http_lua_module模块立刻调度执行
# 入参func为线程中执行的函数,arg为func需要的参数,返回对象为Lua thread
ok, res1, res2, ... = ngxthread.wait(thread1, thread2, ...)
# 等待1个或者多个轻量级线程执行完毕后返回,ok表示所有线程是否正常结束,接下来的res返回值按照次序是每个func的返回值
ok, err = ngxthread.kill(thread) # 杀死正在运行的轻量级线程,仅父线程可以执行
coroutine # 创建Lua协程,它不会自动执行
resume # 执行已创建还未执行的协程,或者恢复执行被yield挂起的协程
yield # 挂起当前执行的协程。
wrap # 创建Lua协程,可通过调用返回函数实现在协程中执行传入的方法(类似resume)
running # 返回正在执行的协程对象
status # 返回协程的状态,包括running、suspended、normal、dead
同步非阻塞的sleep方法及lua-resty-lock锁
ngx.sleep(seconds) # 对当前Lua代码块睡眠一段时间,seconds可以精确到0.001 (即毫秒)。它是通过Nginx中的定时器实现,不会阻塞Nginx运行
# lua-resty-lock模块的方法
obj, err = lock:new(dict name, opts)
# 自共享内存字典中新建锁对象.
# opts选项包括
# - exptime: 设置锁的过期时间,到期后自动释放锁。默认30秒,精确到毫秒
# - timeout: lock方法的最大等待时间,默认5秒,精确到毫秒
# - step 锁是由ngx.sleep方法实现的,step定义了第一次sleep的时间。默认0.001秒。
# - ratio:定义每轮sleep时间的增长率,默认为2
# - max_step:定义了最大sleep时间,默认为0.5秒
elapsed err = obi:lock(key) # 获取关于key的锁,返回等待的时间,如果未获取到锁则返回nil
ok. err = obi:unlock() # 释放锁
ok, err = obi:expire(timeout) # 重置new方法传入的timeout选项
9.定时器及时间相关的SDK
hdl, err = ngx.timer.at(delay, callback, user_arg1, user_arg2, ...)
# 新增定时器,定时器触发后执行callback函数,user arg是函数的参数。
# delay是延迟执行时间,单位为秒,精确到毫秒。0表示立刻在后台的轻量级线程中执行callback
# callback的第一个参数是布尔值的premature,它表示定时器是否过早被触发,例Nginx worker进程在关闭时并不会等待定时器触发,而是直接调用callback,此时premature值为true,接下来才是user arg等参数
# callback由于没有当前请求,故不能使用子请求类API(如ngx.location.capture),也不能获取当前请求的参数,如ngx.req.
ngx.timer.every # 类似at,相当于每daley秒就调用一次callback
ngx.timer.running_count # 当前正在执行的定时器数量
ngx.timer.pending_count # 等待执行的定时器数量
## 定时器相关指令
lua_max_pending_timers # 指定API: ngxtimer.at中等待执行的定时器的最大数目,达到后新增的定时器直接返回nil,默认值1024
lua_max_running_timers # 指定API: ngxtimer.at中正在执行回调方法的定时器最大数目,达到后新增的定时器的回调方法不会被执行,在error.log中出现N lua max running timers are not enough字样的日志,默认256
## 获取请求的处理时间SDK
ngx.req.start_time # 返回开始处理当前请求经过的时间
## 时间类SDK
ngx.today # 从Nginx中取当前时间的日期(格式为yyyy-mm-dd)
ngx.time # 取得自epoch时间 (1970年1月1日0点)到现在的秒数
ngx.now # 取得自epoch时间 (1970年1月1日0点)到现在的秒数,精度到0.001 (毫秒)
nex.update # time·更新Nginx的缓存时间
nex.localtime # 返回本地时区下的当前时间(格式为yyyy-mm-dd hh:mm:ss)
ngx.utctime # 返回UTC下的当前时间(格式为yyyy-mm-dd hh:mm:ss)
ngx.cookie time(sec) # 将epoch时间秒数转换为cookie中可以识别的时间格式,如ngxcookie time1547466623) 返回Mon.14-Jan-19
ngx.http time(sec) # 将epoch时间秒数转换为HTTP头部中的标准时间格式,例如nx.http time(1547466623) 返回Mon14Jan 2019GMT:2019-01-14
ngx.parse http time(str) # 将HTTP头部标准时间格式的字符串转换为epoch时间秒数
10.share.DICT基于共享内存的字典
基于共享内存的字典shared dict
lua_shared_dict
- 指令 :基于Nginx的共享内存(使用Slab管理器)实现的跨worker进程字典容器,支持LRU淘汰功能。由于reload不会清除共享内存中的内容,故reload后shared dict值仍存在
- SDK : 共享内存的所有方法都是原子的、线程安全的
11.shared dict的SDK
value, flagr = ngxshared.DICT:get(key)
# 返回字典中未过期key的值
value. flags, stale = nex.shared.DlCT:eet stale(key)
# 返回字典中key的值 (无论是否过期)
success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags?)
# 向字典中设置键值对,当内存不足时会按过期时间淘汰老的元素。当连续淘汰30个(当前版本)元素后仍然无法分配出足够内存,则返回失败
ok, err = ngx.sharedDICT:safe set(kev, value, exptime?, flags?)
success, err, forcible = ngx.shared.DICT:add(key, value, exptime?, flags?)
# 类似set,但仅向字典中添加新的键值对,若key已经存在则返回false,”exists”,false
ok, err = ngx.shared.DICT:safe add(key, value, exptime?, flags?)
# 类似add,但不会在内存不足时根据LRU强行淘汰未过期的键。内存不足时,返回nil和”o memory”
success, err, forcible = ngx.shared.DICT:replace(key, value, exptime?, flags?)
# 类似set,但仅能覆盖字典中已经存在的键值。若kev不存在,则返回false,”not found”.false
ngx.shared.DICT:delete(key)
# 从字典中删除key及对应的值,等价于set(key,nil)
newval, err, forcible? = ngxshared.DICT:incr(key, value, init?, init ttl?)
# 如果字典中key对应的值存在,则将其值加上value,并返回相加后的值newval。若kev在字典中对应的值不是数字,则返回nil’not a number”false
length, err = ngx.shared.DICT:rpush(key, value) # 类似lpush,但却是从队列的尾部插入元素
val, err = ngxshared.DICT:lpop(key) # 从key对应的队列头部取出1个元素
val, err = ngx.shared.DICT:rpop(key) # 从key对应的队列尾部取出1个元素
len. err = ngx.shared.DICT:llen(key) # 查询key对应的队列的元素个数。若key对应的值不是队列,则返回nil,”value not a list”
ttl, err = ngx.shared.DICT:ttl(key) # 返回key对应的元素距离过期时间的剩余秒数,如果是永不过期类型则返回tt1为0
success, err = ngx.shared.DICT:expire(key, exptime) # 设置key对应元素的过期时间,如果key对应元素不存在则返回nil,not found”。exptime为0则表示永不过期
ngx.shared.DICT:flush all() # 标记字典中所有元素皆为过期.
flushed = ngx.shared.DICT:flush expired(max count?) # 将字典中最多max count(0表示不做限制)个过期元素释放内存
keys = ngx.shared.DICT:get keys(max count?) # 将字典中最多max count个键取出,max count默认值1024,设置为0时表示取出所有的键
capacity_bytes = ngx.shared.DICT:capacity() # 以字节数返回共享内存大小。须require"resty.core.shdict'
free_page_bytes = ngx.shared.DICT:free space # 以字节数返回共享内存slab管理器中空闲页的大小。须require"resty.core.shdict"
12.子请求的使用方法
Nginx中的主请求与子请求
子请求的生命周期 : 依赖父请求
主请求与父子请求间的关系
- 主请求即客户端发来的请求
- 主请求可派生多个子请求
- 子请求也可派生多个子请求
- 子请求完成时会激活父请求
子请求的响应如何处理
- 作为父请求响应 : postpone filter过滤模块
- 放置内存中等待模块处理
location.capture子请求
ngx.location.capture(uri, option)
# 生成Nginx子请求,且接下来的Lua代码会以同步的方式等待子请求返回后再执行。因此,该方法执行前应读取完整的请求包体,防止读取请求包体超时发生。子请求继承当前请求所有头部。返回值依赖子请求的响应,且响应都会放置在内存中,故子请求返回的响应不应过大,对于大文件响应可以使用
ngx.location.capture_multi(ffuri, option,furi, option?....?)
# 支持并行发起多个子请求,返回值代表的子请求响应与参数中的uri顺序相同仅所有子请求皆获得响应后,当前方法所属的代码段才会继续执行
ngx.is_subrequest # 以布尔值返回当前请求是否为子请求
13.基于OpenResty的WAF防火墙
WAF(Web Application Firewall)防火墙 github.com/loveshell/n…
lua_package_path "/usr/local/nginx/conf/waf/?.lua";
lua_shared_dict limit 10m;
init_by_lua_file /usr/local/nginx/conf/waf/init.lua;
access_by_lua_file /usr/local/nginx/conf/waf/waf.lua;