OpenResty 开发速览

2019-01-24 09:42编辑本页

简介

Openresty 是一个基于 Nginx 和 Lua 的高性能 Web 平台。其主要组成部分为:

  1. Nginx1
  2. Lua 虚拟机
  3. lua-nginx-module: 将 Lua 虚拟机嵌入 Nginx 中,并提供Nginx API 供 Lua 调用的项目。在 Lua 层面可以通过使用这些 API 达到非阻塞的效果,这主要归功于 cosocket 和 nginx event 模型
  4. stream-lua-nginx-module: 与 lua-nginx-module 功能相似,区别是 lua-nginx-module 提供的是 nginx 的 http 模块的 API,而 stream-lua-nginx-module 提供的是 nginx 的 stream 模块的 API。
  5. lua-resty-core: 使用 FFI,提供了一系列 Lua 层面通用的 API
  6. lua-resty-*: 在以上几个模块的基础上,封装了一系列常用服务的模块。如: lua-resty-redis/lua-resty-mysql/lua-resty-http

开发

在基于 Openresty 的开发中,要同时了解 Nginx 和 Lua 的工作原理,再去进一步了解 lua-nginx-module 的原理。这样才能有一个在性能与可维护性之间良好的平衡的项目。

1. Nginx

在基于 Openresty 的开发中,首先要认清 Nginx 在整个项目中的角色。典型的角色有如下几种:

  1. 反向代理服务器
  2. Web Server 本身
  3. 正向代理服务器

考虑到使用 Openresty 开发的项目一般偏向服务端,故在这里只讨论前两种情况

1.1 反向代理

这是 Nginx 的典型用法,即作为网关或者负载均衡的前端,直接接收外部请求,做简单处理后,使用 upstream 功能将流量代理至后端。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
+---------+---------+    +------------------------+
|         |         |    |                        |
|         |         |    |    Web server          |
|         |         |    |                        |
|         |         |    +------------------------+
|         |         |
|         |         |    +------------------------+
|         |         |    |                        |
|   Lua   |Upstream |    |    Web server          |
|         |         |    |                        |
|         |         |    +------------------------+
|         |         |
|         |         |    +------------------------+
|         |         |    |                        |
|         |         |    |    Web server          |
|         |         |    |                        |
+---------+---------+    +------------------------+

画一个典型的角色框架图。其中将 Openresty 的部分抽象为由 Lua 负责的网关逻辑层面,和由 Nginx 负责的 Upstream 功能的层面。

这里还是着重讲 Nginx 在这种架构中的作用:

  1. 接受请求
    1. 若 nginx 工作在 master-worker 多进程模式下,多个 worker 进程监听同一端口,就要面临一个 “惊群”问题:即多个进程同时被连接事件唤醒,但实际处理连接的只是其中一个进程。这里 nginx 采用了 ngx_accept_mutex 同步锁的机制来解决该问题。2
    2. 多进程之间的负载均衡问题:使用负载阈值来表示进程的负载情况,从而动态平衡多进程之间的负载
  2. 简单的路由功能:这里主要指的是 nginx 配置文件中设置的多个等级的关键字,如 http/server/location,提供简单的路由功能(当然也可以设置复杂的路由,不过我倾向于将复杂的路由部分交给 Lua 代码来完成)
  3. 提供hook 点,供 lua-nginx-module 载入 lua 虚拟机及代码:这里就是指 Nginx 的模块挂载点了。这些挂载点将请求的声明周期细分为多个阶段,每个阶段又有明确的作用,方便程序的模块化管理。3上述的阶段细分是提供给 nginx module 开发者的,nginx module 在进行一个模块的具体功能开发前大致有几样要做的事情:初始化配置指令数据结构、模块上下文、处理配置指令冲突、注册模块,然后这时才能开始开发模块的具体功能。而 openresty 在提供了多个 hook 点,并在相应的 hook 点暴露了适当的 API 之后,开发的成本大幅降低
  4. upstream 功能

1.2 Web Server

在这种架构中,我一般的做法是:

将简单的路由部分交给 Nginx 配置文件,再将响应用户请求的 Lua 程序通过 lua-nginx-module 提供的挂载点运行起来

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
server {
    listen 80;
    server_name example.com;

    location /backend/ {
        content_by_lua_block {
            local handler = require "handler"
            handler.go()
        }
    }
}

如果这里需要使用现成的 web 框架的话,可能会看到 Vanilla 之类的框架。

我个人还是更倾向于多种轻量级的模块综合:

  1. 路由部分使用简单的 lua table
  2. 在渲染页面或者结果时使用 openresty/lemplate
  3. 访问数据库或者发起 http 请求的话就使用 lua-resty-* 系列的模块

这样开发出来的 web server 优点如下:

  1. 打包方便,可以不用 luarocks 工具(其实 openresty 官方提供了 opm 工具来解决 lua-resty-* 的依赖)
  2. 部署方便
  3. 简单的模块化

2. Lua

这里有 Lua 源码的简单解析,就不再赘述了。

lua 源码系列文章:

  1. 使用 table.new 来申请已知大小的 lua 表,因为 table.new 调用的 lua_createtable() 函数在 LuaJIT 中是可以被优化的。还有一个优点是预分配了表的大小,防止了表在增长时的资源消耗。
  2. 使用本地变量将由 lua-nginx-modulelua-resty-core 提供的 API 返回的结果缓存。可以通过减少不必要的栈操作来减少消耗。
  3. 注意 lua 中的 __gc 元表方法在某些模块中的应用,防止由于触发 __gc 而导致的奇特BUG4
  4. 注意在 openresty 中,lua 层面的代码应当避免IO的阻塞:如使用 lua 原生的 os 库读写本地文件、系统调用,这会影响整个 nginx worker 的运行

3. lua-nginx-module

这里主要讲的是 lua-nginx-module 中的一些重要配置。

  • lua_code_cache: on|off: 关掉代码缓存的结果就是每个请求运行一个单独的 Lua VM 实例,即对 lua 代码的改动可以即时生效。这个特性建议仅在调试时打开。有些模块的功能可能会依赖于代码的缓存
  • lua_package_path / lua_package_cpath 这两个指令直接决定了 nginx 是否能找到你要引用的 lua 模块,所以非常重要

其他的指令文档也可以通过 openresty/lua-nginx-module 官方文档清晰的看到。

4. 工具

  1. openresty/openresty-devel-utils: 在这个库里有许多 openresty 相关开发过程中要使用的一些方便的小工具,如:
    1. lua-releng: 对 luac 命令行的一个封装,将多个 openresty 提供的全局变量纳入正确范畴内
    2. reindex: 主要是针对基于 Test::Nginx模块下的测试文件的一个语法格式检查
  2. spacewander/luacov-console: 与 luacov 工具结合,在终端生成彩色的代码覆盖率,稍作处理就可以当做 CI 工具链中的代码覆盖率数据来源
  3. Test::Nginx: 是 openresty 官方在用的数据驱动的测试框架

See Also

  1. OpenResty 最佳实践
  2. openresty/lua-nginx-module / openresty/stream-lua-nginx-module / openresty/lua-resty-core 官方文档

  1. 使用过的历史版本为: 1.11.7 -> 1.13.6。升级的重要原因是:stream-lua-nginx-module 加入 udp server 的支持 ↩︎

  2. mutex 同步锁:(TODO) ↩︎

  3. 在 http 相关模块中的挂载点有: NGX_HTTP_POST_READ_PHASE: /* 读取请求内容阶段 / NGX_HTTP_SERVER_REWRITE_PHASE:/ Server请求地址重写阶段 / NGX_HTTP_FIND_CONFIG_PHASE: / 配置查找阶段: / NGX_HTTP_REWRITE_PHASE: / Location请求地址重写阶段 / NGX_HTTP_POST_REWRITE_PHASE: / 请求地址重写提交阶段 / NGX_HTTP_PREACCESS_PHASE: / 访问权限检查准备阶段 / NGX_HTTP_ACCESS_PHASE: / 访问权限检查阶段 / NGX_HTTP_POST_ACCESS_PHASE: / 访问权限检查提交阶段 / NGX_HTTP_TRY_FILES_PHASE: / 配置项try_files处理阶段 / NGX_HTTP_CONTENT_PHASE: / 内容产生阶段 / NGX_HTTP_LOG_PHASE: / 日志模块处理阶段 */ ↩︎

  4. __gc 元表方法相关问题举例 https://github.com/openresty/lua-resty-lock/issues/20 ↩︎

除另有声明外 本博客文章均采用 知识共享(Creative Commons) 署名 4.0 国际许可协议 进行许可 转载请注明原作者与文章出处


标签: openresty lua

点击加载Disqus评论
Creative Commons © 2013 — 2023 xiaocang | Theme based on fzheng.me & NexT | Hosted by Netlify