OpenResty:Nginx与lua基础

OpenResty 的两个基石:NGINX 和 LuaJIT。

 

在 OpenResty 的开发中,我们需要注意下面几点:

  • 要尽可能少地配置 nginx.conf;
  • 避免使用if、set 、rewrite 等多个指令的配合;
  • 能通过 Lua 代码解决的,就别用 NGINX 的配置、变量和模块来解决。

这样可以最大限度地提高可读性、可维护性和可扩展性。

 

下面这段 NGINX 配置,就是一个典型的反例,可以说是把配置项当成了代码来使用,在使用 OpenResty 进行开发时需要避免。

location ~ ^/mobile/(web/app.htm) {
    set $type $1;
    set $orig_args $args;
    if ( $http_user_Agent ~ "(iPhone|iPad|Android)" ) {
        rewrite ^/mobile/(.*) http://touch.foo.com/mobile/$1 last;
    }
    proxy_pass http://foo.com/$type?$orig_args;
}    

 

NGINX 配置

NGINX 通过配置文件来控制自身行为,它的配置可以看作是一个简单 的 DSL。NGINX 在进程启动的时候读取配置,并加载到内存中。如果修改了配置文件,需要重启或者重载 NGINX,再次读取后才能生效。只有 NGINX 的商业版本,才会在运行时, 以 API 的形式提供部分动态的能力。

如下为一段简单的nginx配置文件:

worker_processes auto;
pid logs/nginx.pid;
error_log logs/error.log notice;
worker_rlimit_nofile 65535;

events {
    worker_connections 16384;
}

http {
    server {
        listen 80;
        listen 443 ssl;
        location / {
            proxy_pass https://foo.com;
        }
    }
}                    

stream {
    server {
        listen 53 udp;
}    

  

基础概念:

1.每个指令都有自己适用的上下文(Context),也就是NGINX配置文件中指令的作用域。

最上层的是 main,里面是和具体业务无关的一些指令,比如上面出现的 worker_processes、pid 和 error_log,都属于 main 这个上下文。另外,上下文是有层级关系的,比如 location 的上下文是 server, server 的上下文是 http,http 的上下文是 main。

指令不能运行在错误的上下文中,NGINX 在启动时会检测 nginx.conf 是否合法。比如把 listen 80; 从 server 上下文换到 main 上下文,然后启动 NGINX 服务,会看到类似这样的报错:

"listen" directive is not allowed here ......

2.NGINX 不仅可以处理 HTTP 请求 和 HTTPS 流量,还可以处理 UDP 和 TCP 流量。

其中,七层的放在 HTTP 中,四层的放在 stream中。在 OpenResty 里面, lua-nginx-module 和 streamlua-nginx-module 分别和这俩对应。

 

NGINX 支持的功能,OpenResty 并不一定支持,需要看 OpenResty 的版本号。

OpenResty 的版本号是和 NGINX 保持一致的,所以很容易识别。比如 NGINX 在 2018 年 3 月份发布的 1.13.10 版本中,增加了对 gRPC 的支持,但 OpenResty 在 2019 年 4 月份时的最新版本是 1.13.6.2,由此 可以推断 OpenResty 还不支持 gRPC。

 

MASTER-WORKER 模式

NGINX 启动后,会有一个 Master 进程和多个 Worker 进程(也可以只有一个 Worker 进程)。

OpenResty:Nginx与lua基础

 

Master 进程,扮演“管理者”的角色,并不负责处理终端的请求,它是用来管理 Worker 进程的,包括接受管理员发送的信号量、监控 Worker 的运行状态。当 Worker 进程异常退出时, Master 进程会重新启动一个新的 Worker 进程。

Worker 进程则是“一线员工”,用来处理终端用户的请求。它是从 Master 进程 fork 出来的,彼此之间相互独立,互不影响。多进程的模式比 Apache多线程的模式要先进很多,没有线程间加锁,也方便调试。即使某个进程崩溃退出了,也不会影响其他 Worker 进程正常工作。

OpenResty 在 NGINX Master-Worker 模式的前提下,又增加了独有的特权进程(privileged agent)。 这个进程并不监听任何端口,和 NGINX 的 Master 进程拥有同样的权限,所以可以做一些需要高权限才能 完成的任务,比如对本地磁盘文件的一些写操作等。

如果特权进程与 NGINX 二进制热升级的机制互相配合,OpenResty 就可以实现自我二进制热升级的整个流程,而不依赖任何外部的程序。

减少对外部程序的依赖,尽量在 OpenResty 进程内解决问题,不仅方便部署、降低运维成本,也可以降低程序出错的概率。可以说,OpenResty 中的特权进程、ngx.pipe 等功能,都是出于这个目的。

 

执行阶段

执行阶段也是 NGINX 重要的特性,与 OpenResty 的具体实现密切相关。NGINX 有 11 个执行阶段,可以从 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 也有 11 个 *_by_lua指令,它们和 NGINX 阶段的关系如下图所示(图片来 自 lua-nginx-module 文档):

OpenResty:Nginx与lua基础

 

 

其中, init_by_lua 只会在 Master 进程被创建时执行,init_worker_by_lua 只会在每个 Worker 进程被创建时执行。其他的 *_by_lua 指令则是由终端请求触发,会被反复执行。

 

上一篇:OpenResty究竟解决了什么痛点


下一篇:IBM Cloud CDN - 流媒体加速(拉流场景)实战