在提到 OpenResty 的时候相信大部分人都是对这个词或多或少有些陌生,但是如果在云原生领域听到国外的 Kong 或国内的 Apisix 网关时,大家可能又很熟悉了,然而这两大云原生网关都是基于 OpenResty 进行开发的,所以在进一步解读 Apisix 源码之前,我们有必要先了解一下 OpenResty 这个框架。
介绍
OpenResty
OpenResty 是一款以 Nginx 的一个 C 模块(lua-nginx-module,该模块将 LuaJIT 嵌入到 Nginx 中,并对外提供一套完整的 Lua API)进行扩展,以 Lua 为主要开发语言的 Web 框架。
它能够让你的 Web 服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
使用户既能完美地利用 Nginx 的高性能和高并发的优势,又能拥有 Lua 脚本语言的开发效率和迭代速度。
LuaJIT
OpenResty 使用 LuaJIT 而不是标准 Lua 作为执行环境。
或许有读者认为 LuaJIT 只是给 Lua 内置了一个 JIT 编译器,但实际上 LuaJIT 与 Lua 并不是一回事,LuaJIT 只是兼容了 Lua 5.1 的语法,而 Lua 目前最新版本已经更新到了 5.4,LuaJIT 的最新版本则是 2.1.0-beta3。
在 OpenResty 几年前的老版本中,编译的时候,你可以选择使用标准 Lua VM,或者 LuaJIT VM 来作为执行环境,不过,现在已经去掉了对标准 Lua 的支持,只支持 LuaJIT。
标准 Lua 出于性能考虑,它内置了虚拟机,所以 Lua 代码并不是直接被解释执行的,而是先由 Lua 编译器编译为字节码(Byte Code),然后再由 Lua 虚拟机去执行。
而 LuaJIT 的运行时环境,除了一个汇编实现的 Lua 解释器外,还有一个可以直接生成机器代码的 JIT 编译器。开始的时候,LuaJIT 和标准 Lua 一样,Lua 代码被编译为字节码,字节码被 LuaJIT 的解释器解释执行。
但不同的是,LuaJIT 的解释器会在执行字节码的同时,记录一些运行时的统计信息,并通过一些方式,找到经常被执行到的热代码,将这些热代码通过 JIT 编译器进行编译。
编译的过程,是把 LuaJIT 字节码先转换成 LuaJIT 自己定义的中间码(IR),然后再生成针对目标体系结构的机器码,来提高这些热代码的执行效率。
并且出于性能考虑,OpenResty 自己维护了一套 LuaJIT 分支,扩展了许多独有的 API(后续文章中提到的 LuaJIT 都指 OpenResty 自己维护的 LuaJIT 分支)。
使用
在 上一篇 文章中,我们通过 Homebrew 安装了 OpenResty,此处以 1.19.9.1_2
版本为例,Homebrew 会将 OpenResty 默认安装在 /usr/local/Cellar/openresty/1.19.9.1_2/
目录下,而 OpenResty 内置的 LuaJIT 则放在 /usr/local/Cellar/openresty/1.19.9.1_2/luajit/
目录下 。
安装成功后,OpenResty 提供 CLI 工具 resty
以及用来启动服务的两个命令 openresty
和 nginx
,这两个命令完全等价,用户选择其中任意一个即可(后续文章默认都使用 openresty
),而 Apisix 的启动则是通过 openresty
这个命令来完成的。
openresty
此处我们简单介绍一下 openresty
命令。
openresty
命令常用的参数有 3 个。-s
、-c
以及 -p
。
-s
代表发送到 OpenResty 的指令,常用的有 stop
、reload
,用来指代停止服务以及重启服务。
-c
是用来指定 nginx.conf
文件具体路径的,代表启动 OpenResty 时读取哪个 Nginx 配置文件。
-p
是用来指定文件前缀的,在指定了文件前缀后,OpenResty 启动时则会基于该前缀去拼接对应的 Nginx 配置文件、日志文件地址等。
在运行 openresty
命令后,访问 localhost:80
出现如下页面则代表 OpenResty 启动成功。

resty
CLI 工具 resty
则可以用来直接执行一些比较简单的命令,如下所示。
$ resty -e "ngx.say('hello world')"hello world
前面我们提到 OpenResty 是基于 Nginx 来运行的,这个在 CLI 中也得到了很好的体现,我们来深入探究一下 CLI 命令的执行原理。
$ resty -e "ngx.say('hello world'); ngx.sleep(60)" &
我们使用上述命令使程序运行后进入休眠状态而不是直接退出,再来观察它背后究竟是怎样运行的。
$ ps -ef | grep nginx501 14778 14776 0 11:12AM ttys010 0:00.02 usr/local/Cellar/openresty/1.19.9.1_2/nginx/sbin/nginx -p tmp/resty_VyKROmMFyF/ -c conf/nginx.conf
它本质上就是创建了一个临时的 Nginx 配置文件并运行了 openresty -p xxx -c xxx
(上文提到 nginx
命令与 openresty
命令等价)来开启了一个临时的 OpenResty 服务。而在命令退出之前,我们最后来看看 /tmp/resty_VyKROmMFyF/conf/nginx.conf
这个文件。
$ cat tmp/resty_VyKROmMFyF/conf/nginx.confdaemon off;# 省略部分配置http {access_log off;lua_socket_log_errors off;resolver 192.168.1.1 192.168.9.9 ipv6=off;lua_regex_cache_max_entries 40960;init_by_lua_block {# 省略代码}init_worker_by_lua_block {# 省略代码}}
在该文件中,它初始化了两个执行阶段的代码,init_by_lua_block
以及 init_worker_by_lua_block
,而这两个阶段是 OpenResty 启动时的两个初始化阶段,我们将在后续文章中进行详细解读。并且由于篇幅原因,此处省略了这两个阶段对应的代码,读者可以自行操作后进行查看。
结束语
本篇文章我们简单介绍了 OpenResty。当然一篇文章能描述的也只能是这个框架的冰山一角,我这里推荐感兴趣的读者阅读温铭老师写的《OpenResty 最佳实践》来做更进一步的了解。




