5

今年的 OpenResty Con 在北京举行,考虑到路途过于遥远,我决定看直播。虽然参与的方式变了,但我依旧跟去年一样,趁着记忆还算清晰写了篇文章,算是“路边社”的新闻稿吧。

第一个演讲者是来自于 Strikingly 的龚凌晖,讲的是 Strikingly 内部用 OpenResty 实现的缓存系统。Strikingly 通过 OpenResty 在应用服务器前面实现了一层代理。他们把缓存数据放置在 S3/OSS 上,元数据放在 Redis 上。请求先打到 OpenResty 集群上,如果对应的缓存可用,就直接返回缓存结果,否则再访问源站并更新缓存。这个有点像放大版的 Nginx proxy_cache。凌晖的演讲有一点挺有趣的,他提到在当初做技术选型时,比较过基于 OpenResty 开发和自己造轮子两个方案。OpenResty 相关的库少,而且剩下的库还是良莠不齐的;然而自己造轮子开发量比较大,需要重新实现不少功能。相比之下,用 OpenResty 就只用造相关的库,虽然轮子还是要造的,但是工作量少了不少。

之后演讲的是来自 Kong 的 Thibault Charbonnier。Kong 公司是 OpenResty 的重度使用者,经常能看到他们的工程师分享关于 OpenResty 的演讲/文章。Thibault 本人就给 OpenResty 项目贡献了不少代码。演讲的内容是 Kong 工程师用 OpenResty 重写过去由其他软件提供的功能,使得 Kong API Gateway 这个产品变得越来越“开箱即用”的过程。过去 Kong API Gateway 除了用到 OpenResty 之外,还要打包用于生成 uuid 的 libuuid、用于进程间通讯的 serf、用于 DNS 查询的 dnsmasq,等等。这一连串玩意无论是部署还是维护起来都挺麻烦,尤其考虑到 Kong 公司做的是 To B 的产品,这种麻烦可能还要乘个系数翻几番。后面他们在 OpenResty 里面实现了 uuid 的功能(lua-resty-jit-uuid),写了 lua-resty-dns-client 这个库来进行 DNS 查询,进程间通讯的逻辑也改成在 OpenResty 内部完成。这下就把其他乱七八糟的小软件都砍掉了,仅剩 OpenResty 和数据库两种组件。作为同样做 To B 商业软件的程序员,我对这种对简化工具栈的追求深有同感。如何适配客户花样百出的现场环境,一直是做 To B 产品的一大难题。既然无法“店大欺客”地要求客户提供特定的环境,就只能遵循 less is more 的原则,尽量把软件栈做简单来避免意料之外的问题。除了讲述 Kong API Gateway 开发过程中大刀阔斧精兵简政的故事外,Thibault 还提到了他们开源出来的 worker 通讯库 lua-resty-worker-events、双层缓存库 lua-resty-ml、测试辅助库 lua-resty-busted 等等。

上午最后一个演讲是春哥介绍 OpenResty Inc. 的商业产品,也算是之前他一直唠叨的小语言的首次公开亮相吧。春哥展示了用于描述网关规则的 edgelang,语法看上去像是 erlang,能够根据模式匹配执行对应的动作。还有一个类似于 test-nginx 语法的,用于驱动测试集 testml。(有趣的是,testml 是在线上节点运行的,让我想起那个“true programmer tests in production”的段子……)此外还有个 schemalang,用于生成接口部分的代码,类似于 Swagger Api Spec。这些小语言都是用 fanlang 这门小语言开发的,最后会被翻译成 Lua 代码和 Nginx 配置。春哥还提到他们的产品力求以最小的组件获取最多的功能。跟其他 CDN 公司的技术栈不同,OpenResty Inc. 只用到了 OpenResty 和 PostgreSQL,连消息总线和内存数据库都是在 OpenResty 内部实现的。看来尽量减少组件数,确实是 To B 的公司的共识。

下午的第一个演讲,是 OpenResty Inc. 技术合伙人孙大同做的,关于新的 stream-lua-nginx-module 的介绍。为什么说是新的呢?之前 Nginx 1.9 的时候,春哥曾经基于 lua-nginx-module 改过一个 stream-lua-nginx-module。但由于 Nginx 官方在 1.13 大幅修改了 Nginx stream 子系统,原来的 stream 系统已经无法继续维护下去了。所以目前的 stream-lua-nginx-module 是基于 1.13 版的 stream 子系统重写的。大同介绍了 stream 子系统的一些功能,当然更为重要的是相关的坑。

第一个坑是 stream 系统在处理 UDP 时,是逐个包逐个包转发,性能可能会不太理想。其次由于 stream 工作在较为底层的部分,所以 LuaJIT 的 GC 对性能的影响更为明显。

另外在处理 TCP 连接过程中,由于连接的生命周期较长,频繁的内存分配可能会导致内存占用居高不下。stream 子系统的代码我没有看过,http 子系统里面,每个请求分配的内存只有在请求体生命周期结束时才会回收,不知道内存占用是否也由相似的原因导致的?

还有一个坑,stream 操作需要避免在未读完 socket 的读缓冲区前调用 close,因为 OS 会发送一个 RST 包而不会跟预期一样发送响应(对该现象的详细解释)。这个问题在 http 子系统中没有暴露出来,但是如果你需要跟更低层级的协议打交道 -- 你需要看下他们为此新增的 shutdown 接口。

大同还提到 stream 子系统中的 ngx.print/say 和直接操作 ngx.req.socket 会有冲突,需要调用 ngx.flush 及时输出响应。

最后有一个重要的点,stream-lua-nginx-module 是由 meta-lua-nginx-module 里面的 tt2 模板文件编译出来的。用模板来生成代码有利于同时保证 http 版本和 stream 版本能够及时同步,不过如果有修改,那么需要先改 C 代码,测试通过后再反哺回模板文件,貌似会比较麻烦?

紧跟在大同之后的演讲,是 codedump 老师关于 Lua 5.1 GC 实现的介绍。LuaJIT 虽然跟 Lua 5.1 有天壤之别,但是 GC 这一块却是大同小异的。codedump 老师讲解了 Lua 之中的增量标记-清扫回收算法,里面用的是经典的三色标记(未标记对象为白、正在标记对象为灰、子对象全部标记完毕的对象为黑,垃圾回收时回收白色对象)。美中不足的是,可能由于时间关系,细节部分没有更好地说明,比如为什么 table 的屏障和其他对象不同等等。如果对这些细节感兴趣,可以看下 codedump 老师自己写的一篇文章:Lua GC。随便吐嘈下 LuaJIT 计划的新一代GC 什么时候能够实现呢?现行的 GC 实现跟其他高性能语言差距挺大的呢。也许新的 LuaJIT 维护者会带来一些改变。随便解答下 Q&A 阶段那个标记-清扫算法如何解决循环依赖的问题:如果循环依赖的多个对象中,每一个对象都没有被其他对象引用,则初始化阶段后这几个对象一直都是白色,于是就能被回收了。

下一个演讲者是李凯,快乐茄的后台研发工程师。因为他的演讲偏向于具体的业务,所以我也没什么记录的…… 印象里他问了个为什么用 ngx.timer.at(0, ...) 会比 ngx.thread.spawn 更占用资源的问题,然后孙大同回答,说是前者(timer 对象)会创建一个包含 Lua Thread 的 fake request 对象,而后者只创建 Lua Thread 相关的对象,所以会更加轻量。

当天收官的演讲者是来自于新浪微博的周晶,OpenResty 社区的老朋友。周晶讲的是当 Motan RPC 框架遇见 OpenResty,两者碰撞出的火花。可惜我对 RPC 框架没什么了解,所以听得云里雾里的。不过周晶提到一个有趣的思路,就是像 PHP 这一类的语言只实现 RPC 的 client,server 部分可以由 OpenResty 负责协议的卸载,然后再以 CGI 的方式转调背后的实现。如果有用 OpenResty 介入 RPC 的需求的同行,建议听下这个演讲。(IT大咖说上有视频回放)


spacewander
5.6k 声望1.5k 粉丝

make building blocks that people can understand and use easily, and people will work together to solve the very largest problems.


引用和评论

0 条评论