OpenResty 中的 Nginx 基础知识

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 的行为

猜你喜欢

转载自blog.csdn.net/weixin_44388689/article/details/131320981