Nginx 版本
OpenResty 的版本,落后于标准 Nginx 版本不少,所以较新的 Nginx 支持的功能,OpenResty 不一定支持。
Nginx 进程模型
当启动 Nginx 后我们使用 ps 来查看相关进程:
$ ps -ef --forest | grep nginx
root 32475 1 0 13:36 ? 00:00:00 nginx: master process /usr/sbin/nginx
-c /etc/nginx/nginx.conf
nginx 32476 32475 0 13:36 ? 00:00:00 _ nginx: worker process
nginx 32477 32475 0 13:36 ? 00:00:00 _ nginx: worker process
nginx 32479 32475 0 13:36 ? 00:00:00 _ nginx: worker process
nginx 32480 32475 0 13:36 ? 00:00:00 _ nginx: worker process
nginx 32481 32475 0 13:36 ? 00:00:00 _ nginx: cache manager process
nginx 32482 32475 0 13:36 ? 00:00:00 _ nginx: cache loader process
Nginx 有且只有一个 Master 进程来执行一些高权限的操作,比如读取配置和绑定端口等,但不负责处理请求,而 Worker 进程的数量一般与宿主机 CPU 逻辑核数保持一致。当 Nginx 启动或重载时,Master 进程会 fork 出 Worker 进程集合,它们之间相互独立,有点像 Chrome 浏览器的标签页,某一个崩溃了并不会影响到其他的。
正式如此的架构赋予了 Nginx 二进制热升级的能力。
热升级通过向旧的 Master 进程发送 USR2 和 WINCH 信号量来启动新的 Master 进程和逐步关掉 Worker 进程。这时如果需要回滚,依旧可以给旧的 Master 发送 HUP 信号量,如果确定不需要回滚,就可以给旧 Master 发送 KILL 信号量来退出。
Nginx 架构官方文档:https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/
Nginx 执行阶段
Nginx 有 11 个执行阶段,可以从源码 https://github.com/nginx/nginx/blob/master/src/http/ngx_http_core_module.h 中看到:
typedef enum {
NGX_HTTP_POST_READ_PHASE = 0,
NGX_HTTP_SERVER_REWRITE_PHASE,
NGX_HTTP_FIND_CONFIG_PHASE,
NGX_HTTP_REWRITE_PHASE,
NGX_HTTP_POST_REWRITE_PHASE,
NGX_HTTP_PREACCESS_PHASE,
NGX_HTTP_ACCESS_PHASE,
NGX_HTTP_POST_ACCESS_PHASE,
NGX_HTTP_PRECONTENT_PHASE,
NGX_HTTP_CONTENT_PHASE,
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
OpenResty 中 *_by_lua
指令与 Nginx 其中 4 个阶段的关系如下图:
这也是 OpenResty 开发时需要时刻对照的一张图。
-
init_by_lua
在 Nginx Master 进程创建时执行,也就是在整个 Nginx 生命周期中只会执行一次通常在
init_by_lua
阶段预先加载 Lua 模块和公共的只读数据,这样可以利用操作系统的 COW (copy on write) 特性来节省一些内存。# this runs before forking out nginx worker processes: init_by_lua_block { require "cjson" } server { location = /api { content_by_lua_block { -- the following require() will just return -- the alrady loaded module from package.loaded: ngx.say(require "cjson".encode{dog = 5, cat = 6}) } } }
-
init_worker_by_lua
在每个 Nginx Worker 进程创建时执行,这个锚点经常用于创建定时器(通过 ngx.timer.at Lua API)。init_worker_by_lua ' local delay = 3 -- in seconds local new_timer = ngx.timer.at local log = ngx.log local ERR = ngx.ERR local check check = function(premature) if not premature then -- do the health check or other routine work local ok, err = new_timer(delay, check) if not ok then log(ERR, "failed to create timer: ", err) return end end end local hdl, err = new_timer(delay, check) if not hdl then log(ERR, "failed to create timer: ", err) return end ';
-
set_by_lua
业务代码的操作通常可以在从
set_by_lua
开始的各阶段完成,推荐根据不同的功能来拆分业务流程。set_by_lua
指令推荐用于设置变量。 -
rewrite_by_lua
转发、重定向
-
access_by_lua
准入、权限
-
content_by_lua
生成返回内容
-
header_filter_by_lua
应答头过滤处理
-
body_filter_by_lua
应答体过滤处理
-
log_by_lua
日志记录
OpenResty lua-nginx-module 文档 https://github.com/openresty/lua-nginx-module/blob/master/README.markdown 对每个指令的用法都有非常详尽的描述。
尤其要注意每个锚点支持的 Nginx context,也就是 Nginx 配置文件中指令的作用域。指令被写在不支持的上下文中会导致 OpenResty 启动失败。
OpenResty 开发原则
- 尽可能少地得配置 nginx.conf
- 避免使用 if、set 、rewrite 等多个指令的配合
- 尽量用 Lua 脚本来控制 Nginx 的行为