OpenResty 你知道的与不知道的

Apr 30, 2020 23:45 · 2001 words · 4 minute read Gateway Nginx OpenResty

OpenResty 是一个基于 Nginx 与 LuaJIT 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

安装

推荐尽量使用 Homebrew 这类包管理工具安装 OpenResty:

$ brew install openresty/brew/openresty

虽然官网也提供了通过源码编译安装的方法,但是不推荐。

正如温铭老师所说,我们的最初目的其实是用开源项目来解决业务需求,不应该浪费时间和环境鏖战,更何况包管理器和容器技术,正是为了帮我们解决这些问题

安装完 OpenResty 后:

$ which resty
/usr/local/bin/resty
$ head -n 1 /usr/local/bin/resty
#!/usr/bin/env perl

resty 即 OpenResty 的 CLI 其实是一个 Perl 脚本。

Hello world

接着我们来看官网提供的新手上路教程,在 Nginx 的配置文件 nginx.conf 中植入了一段 *-by_lua_block 脚本后,启动 /usr/local/bin/penresty (macOS)路径中的 nginx 服务器应用程序。

以下所有命令都是在 macOS 环境下执行,与 Linux 有所不同。

$ ll /usr/local/bin/openresty
lrwxr-xr-x  1 crazytaxii  admin    42B Apr  9 08:21 /usr/local/bin/openresty -> ../Cellar/openresty/1.15.8.3/bin/openresty
ll /usr/local/Cellar/openresty/1.15.8.3/openresty
total 496
-rw-r--r--   1 crazytaxii  admin    22K Apr  9 08:21 COPYRIGHT
drwxr-xr-x   9 crazytaxii  admin   288B Apr  9 08:21 bin
drwxr-xr-x   6 crazytaxii  admin   192B Apr  9 08:21 luajit
drwxr-xr-x   9 crazytaxii  admin   288B Apr  9 08:21 lualib
drwxr-xr-x   5 crazytaxii  admin   160B Apr  9 08:21 nginx
drwxr-xr-x  44 crazytaxii  admin   1.4K Apr  9 08:21 pod
-rw-r--r--   1 crazytaxii  admin   222K Apr  9 08:21 resty.index
$ ll /usr/local/Cellar/openresty/1.15.8.3/openresty/bin
total 328
-rwxr-xr-x  1 crazytaxii  admin    19K Apr  9 08:21 md2pod.pl
-rwxr-xr-x  1 crazytaxii  admin    16K Apr  9 08:21 nginx-xml2pod
lrwxr-xr-x  1 crazytaxii  admin    19B Apr  9 08:21 openresty -> ../nginx/sbin/nginx
-rwxr-xr-x  1 crazytaxii  admin    62K Apr  9 08:21 opm
-rwxr-xr-x  1 crazytaxii  admin    33K Apr  9 08:21 resty
-rwxr-xr-x  1 crazytaxii  admin    15K Apr  9 08:21 restydoc
-rwxr-xr-x  1 crazytaxii  admin   8.3K Apr  9 08:21 restydoc-index
$ ll /usr/local/Cellar/openresty/1.15.8.3/openresty/nginx/sbin
total 3232
-rwxr-xr-x  1 crazytaxii  admin   1.6M Apr  9 08:21 nginx
  • opm 是 OpenResty 的包管理工具
  • restydoc 是一个文档查看工具,和 resty 一样都是 Perl 脚本
  • pod 是 Perl 里的一种标记语言,用于编写文档,pod 目录中存放着 OpenResty、 NGINX、lua-resty-*、LuaJIT 的相关文档。

Nginx C 模块

openresty 指向了一个“特殊的” nginx 二进制可执行文件。

$ openresty -V
nginx version: openresty/1.15.8.3
built with OpenSSL 1.1.1d  10 Sep 2019 (running with OpenSSL 1.1.1g  21 Apr 2020)
TLS SNI support enabled
configure arguments: --prefix=/usr/local/Cellar/openresty/1.15.8.3/openresty/nginx --with-cc-opt='-O2 -I/usr/local/Cellar/openresty/1.15.8.3/openssl/include' --add-module=../ngx_devel_kit-0.3.1rc1 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.06 --add-module=../ngx_coolkit-0.2 --add-module=../set-misc-nginx-module-0.32 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.08 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.15 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.33 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.19 --add-module=../redis2-nginx-module-0.15 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.15 --add-module=../rds-csv-nginx-module-0.09 --add-module=../ngx_stream_lua-0.0.7 --with-ld-opt='-Wl,-rpath,/usr/local/Cellar/openresty/1.15.8.3/openresty/luajit/lib -L/usr/local/Cellar/openresty/1.15.8.3/openssl/lib -Wl,-rpath,/usr/local/Cellar/openresty/1.15.8.3/openssl/lib' --with-pcre-jit --with-http_ssl_module --with-http_realip_module --with-http_stub_status_module --with-http_v2_module --add-module=/private/tmp/openresty-20200409-24642-1qk2p02/kong-build-tools-4.2.2/openresty-build-tools/work/lua-kong-nginx-module --add-module=/private/tmp/openresty-20200409-24642-1qk2p02/kong-build-tools-4.2.2/openresty-build-tools/work/lua-kong-nginx-module/stream --with-stream_realip_module --with-stream_ssl_preread_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module

–add-module 参数后面跟着的以 *-nginx-module 命名的都是 Nginx 的 C 模块。每个模块的源码都在 github 的 OpenResty 组织下 https://github.com/openresty,直接搜索模块名称就可以前往查看相应文档。其中最核心的是 lua-nginx-module 和 stream-lua-nginx-module 分别用于处理七层流量与四层流量。

部分 C 库为 OpenResty 早期使用,但是逐步在被 Lua 库(例如 lua-resty-core)淘汰。

在核心的 lua-nginx-module 中,调用 C 函数的 API,都是使用 Lua C API 来完成的;而 lua-resty-core 中则是把 lua-nginx-module 已有的部分 API,使用 FFI 的模式重新实现了一遍。

我们对比着看一下原生 Nginx:

$ /usr/local/Cellar/nginx/1.17.10/bin/nginx -V
nginx version: nginx/1.17.10
built by clang 11.0.3 (clang-1103.0.32.29)
built with OpenSSL 1.1.1f  31 Mar 2020 (running with OpenSSL 1.1.1g  21 Apr 2020)
TLS SNI support enabled
configure arguments: --prefix=/usr/local/Cellar/nginx/1.17.10 --sbin-path=/usr/local/Cellar/nginx/1.17.10/bin/nginx --with-cc-opt='-I/usr/local/opt/pcre/include -I/usr/local/opt/openssl@1.1/include' --with-ld-opt='-L/usr/local/opt/pcre/lib -L/usr/local/opt/openssl@1.1/lib' --conf-path=/usr/local/etc/nginx/nginx.conf --pid-path=/usr/local/var/run/nginx.pid --lock-path=/usr/local/var/run/nginx.lock --http-client-body-temp-path=/usr/local/var/run/nginx/client_body_temp --http-proxy-temp-path=/usr/local/var/run/nginx/proxy_temp --http-fastcgi-temp-path=/usr/local/var/run/nginx/fastcgi_temp --http-uwsgi-temp-path=/usr/local/var/run/nginx/uwsgi_temp --http-scgi-temp-path=/usr/local/var/run/nginx/scgi_temp --http-log-path=/usr/local/var/log/nginx/access.log --error-log-path=/usr/local/var/log/nginx/error.log --with-compat --with-debug --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-ipv6 --with-mail --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module

与 OpenResty 的 nginx 相比少了那些 C 模块。

LuaJIT

$ ll /usr/local/Cellar/openresty/1.15.8.3/openresty/luajit
total 0
drwxr-xr-x  4 crazytaxii  admin   128B Apr  9 08:21 bin
drwxr-xr-x  3 crazytaxii  admin    96B Apr  9 08:21 include
drwxr-xr-x  7 crazytaxii  admin   224B Apr  9 08:21 lib
drwxr-xr-x  4 crazytaxii  admin   128B Apr  9 08:21 share
$ ll /usr/local/Cellar/openresty/1.15.8.3/openresty/luajit/bin
total 888
lrwxr-xr-x  1 crazytaxii  admin    18B Apr  9 08:21 luajit -> luajit-2.1.0-beta3
-rwxr-xr-x  1 crazytaxii  admin   441K Apr  9 08:21 luajit-2.1.0-beta3

OpenResty 基于自己维护的 LuaJIT 分支 https://github.com/openresty/luajit2。luajit 路径下存放 LuaJIT 可执行文件和依赖。

标准 Lua 出于性能考虑,内置了虚拟机,所以 Lua 代码并不是直接被解释执行的,而是先由 Lua 编译器编译为字节码(Byte Code),然后再由 Lua 虚拟机执行。 而 LuaJIT 的运行时环境,除了一个汇编实现的 Lua 解释器外,还有一个可以直接生成机器代码的 JIT 编译器。开始的时候,LuaJIT 和标准 Lua 一样,Lua 代码被编译为字节码,字节码被 LuaJIT 的解释器解释执行。 但不同的是,LuaJIT 的解释器会在执行字节码的同时,记录一些运行时的统计信息,比如每个 Lua 函数调用入口的实际运行次数,还有每个 Lua 循环的实际执行次数。当这些次数超过某个随机的阈值时,便认为对应的 Lua 函数入口或者对应的 Lua 循环足够热,这时便会触发 JIT 编译器开始工作。 JIT 编译器会从热函数的入口或者热循环的某个位置开始,尝试编译对应的 Lua 代码路径。编译的过程,是把 LuaJIT 字节码先转换成 LuaJIT 自己定义的中间码(IR),然后再生成针对目标体系结构的机器码。

Lua 库

$ ll /usr/local/Cellar/openresty/1.15.8.3/openresty/lualib
total 88
-rwxr-xr-x   1 crazytaxii  admin    30K Apr  9 08:21 cjson.so
-rwxr-xr-x   1 crazytaxii  admin   4.1K Apr  9 08:21 librestysignal.so
drwxr-xr-x  13 crazytaxii  admin   416B Apr  9 08:21 ngx
drwxr-xr-x   3 crazytaxii  admin    96B Apr  9 08:21 rds
drwxr-xr-x   3 crazytaxii  admin    96B Apr  9 08:21 redis
drwxr-xr-x  28 crazytaxii  admin   896B Apr  9 08:21 resty
-rw-r--r--   1 crazytaxii  admin   1.3K Apr  9 08:21 tablepool.lua

lualib 路径下主要存放了 OpenResty 中使用到的 Lua 库。

  • ngx 路径下为 lua-resty-core 项目中的代码,里面是基于 FFI 重新实现的 OpenResty API。因为 FFI 的代码可以被 LuaJIT 优化,所以性能要更好一些。
  • resty 路径下存放的则是各种 lua-resty-* 项目(也即是周边库)中的核心 Lua 代码,提供 Redis、MySQL、memcached、websocket、dns、流量控制等上层封装,都可以在 github 上 OpenResty 组织的 repo 中找到源码。

OpenResty 的技术细节相对隐晦,虽然高质量的文档与完备的测试用例是其最大的亮点之一,但从零开始自行摸索很有可能忽略背后巨大的信息量导致上手困难。