gitbook/OpenResty从入门到实战/docs/137246.md
2022-09-03 22:05:03 +08:00

8.2 KiB
Raw Blame History

50 | 答疑(五):如何在工作中引入 OpenResty

你好,我是温铭。

几个月的时间转瞬即逝到现在OpenResty专栏的最后一个版块微服务 API 网关篇,我们就已经学完了。恭喜你没有掉队,始终在积极学习和实践操作,并且热情地留下了你的思考。

很多留言提出的问题很有价值大部分我都已经在App里回复过一些手机上不方便回复的或者比较典型、有趣的问题我专门摘了出来作为今天的答疑内容集中回复。另一方面也是为了保证所有人都不漏掉任何一个重点。

下面我们来看今天的这 5 个问题。

问题一OpenResty 在工作中的使用

Q快结课了我也基本上跟下来了但自己的实践还是偏少工作中目前未用。不过这确实是很强大的一门课。感谢温老师的持续分享后期工作中我也会择机引入。

A感谢这位同学的认可关于这条留言我想聊一聊如何在工作中引入 OpenResty这确实是一个值得一谈的话题。

OpenResty 基于 Nginx并在它的基础之上加了 lua-nginx-module 的 C 模块和众多 lua-resty 库,所以 OpenResty 是可以无痛替换 Nginx 的,这是成本最低的开始使用 OpenResty 的方法。当然,这个替换过程也是有风险的,你需要注意下面这三点。

第一,确认线上 Nginx 的版本。OpenResty 的主版本号与 Nginx 保持一致,比如 OpenResty 1.15.8.1 使用的就是 Nginx 1.15.8 的内核。如果目前线上 Nginx 的版本号比 OpenResty 的最新版高,那么你最好谨慎换用 OpenResty毕竟OpenResty 升级的速度还是比较慢的,离 Nginx 的主线版本要落后半年到一年的时间。如果线上 Nginx 的版本和 OpenResty 的一致或者比 OpenResty 的低,那就具备了升级的前提条件。

第二,测试。测试是最主要的一个环节,使用 OpenResty 替换 Nginx 的风险很低,但肯定也存在一些风险。比如,是否有自定义的 C 模块需要编译OpenResty 依赖的 openssl 版本,以及 OpenResty 给 Nginx 打的 patch 是否对业务会造成影响等。你需要复制一些业务的流量过来做验证。

第三,流量切换。基本的验证通过后,你还需要线上真实流量的灰度来验证,这时候为了能够快速的回滚,我们可以新开几台服务器来部署 OpenResty而不是直接替换原有的 Nginx 服务。如果没有问题,我们可以选择二进制文件热升级的方式,或者是从 LB 中逐步摘掉和替换 Nginx 的方式来升级。

OpenResty 除了可以替代 Nginx 外,另外两个比较容易的切入点是 WAF 和 API 网关,它们都是对性能和动态有比较高要求的场景,也有对应的开源项目可以开箱即用,我在专栏中也有部分涉及到。

再继续把 OpenResty 深入到业务层面的话,就需要考虑比较多技术之外的因素了,比如是否容易招聘到 OpenResty 相关的工程师?是否能够和公司原有的技术系统进行融合等等。

总的来说,从替代 Nginx 的角度来切入然后慢慢扩散来使用OpenResty ,是一个不错的注意。

问题二OpenResty 的数据库封装

Q根据你的指点要尽量少用 ..字符串拼接特别是在代码热区。但是我在处理数据库访问时需要动态构建SQL语句在语句中插入变量这应该是非常常见的使用场景。可是对于这个需求我目前感觉只有字符串拼接是最简单的办法其他真的想不到既简单又高性能的办法。

A你可以先用我们前面课程介绍过的 SystemTap 或者其他工具分析下,看 SQL 语句的拼接是否是系统的瓶颈。如果不是,自然就没有优化的必要性,毕竟,过早的优化是万恶之源。

如果瓶颈确实是 SQL 语句的拼接,那么我们可以利用数据库的 prepare 语句来做优化,也可以用数组的方式来做拼接。但 lua-resrty-mysqlprepare 的支持一直处于 TODO 状态,所以只剩下数组拼接的方式了。这也是一些 lua-resty 库的通病,实现了大部分的功能,处于能用的状态,但更新得并不够及时。除了数据库的 prepare 语句外,lua-resty-rediscluster 也一直没有支持。

字符串拼接,包括 lua-resty 库的这类问题OpenResty 是希望用 DSL 来彻底解决的——使用编译器的技术自动生成数组来拼接字符串,把这些细节隐藏起来,上层的用户不用感知;使用小语言 wirelang 来自动生成各种 lua-resty 网络通信库,不再需要手写。

这听上去很美好吧?但有一个问题必须正视,那就是自动生成的代码对人类是不友好的。如果你要学习或者修改生成的代码,就必须再学习编译器技术以及一门可能不会开源的 DSL这会让参与社区的门槛越来越高。

问题三OpenResty 的 Web 框架

Q我现在想用 OpenResty 做一个Web项目但做起来很痛苦主要是没找到成熟的框架需要自己造很多轮子就比如说上面的数据库操作问题没找到可以动态构建SQL语句、连贯操作的类库。所以想问下老师在Web框架上有什么好的可以推荐吗

Aawesome-resty 这个仓库中,我们可以看到有专门的 Web 框架分类,有 20 个 开源项目不过大部分项目都处于停滞的状态。其中Lapis、lor 和香草这三个项目你可以尝试下,看看哪一个更适合。

确实,由于没有强大的 Web 框架作为支撑OpenResty 在处理大项目的时候就会力不从心,这也是很少有人用 OpenResty 做业务系统的原因之一。

问题四修改了响应体怎么修改响应头中的content-length

Q如果需要修改respones body的内容就只能在body filter里做修改但这样会引起body长度与 content-length 长度不一致,应该如何处理呢?

A在这种情况下我们需要在 body filter 之前的 header filter 阶段中,把 content length 这个响应头置为 nil不再返回改为流式输出。

下面是一段示例代码:

server {
    listen 8080;

    location /test {
            proxy_pass http://www.baidu.com;
            header_filter_by_lua_block {
                     ngx.header.content_length = nil
            }
            body_filter_by_lua_block {
                    ngx.arg[1] = ngx.arg[1] .. "abc"
            }
     }
}

通过这段代码你可以看到,在 body filter 阶段中,ngx.arg[1] 代表的就是响应体。如果我们在它后面增加了字符串 abc,响应头 content length 就不准确了,所以,我们在 header filter 阶段直接把它禁用掉就可以了。

另外,从这个示例中,我们还可以看到 OpenResty 的各个阶段之间是如何来配合工作的,这一点也希望你注意并思考。

问题五Lua 代码的查找路径

Qlua_package_path 似乎配置的是Lua依赖的搜索路径。对于content_by_lua_file我试验发现它只在prefix下根据指令提供的文件相对路径去搜索而不会到 lua_package_path 下搜索。不知道我的理解对不对?

A这位同学自己动手试验和思考的精神非常值得肯定并且这个理解也是对的。lua_package_path 这个指令是用来加载 Lua 模块而使用的,比如我们在调用 require 'cjson' 时,就会到lua_package_path 中的指定目录中,去查找 cjson 这个模块。而 content_by_lua_file 则不同,它后面跟随的是磁盘中的一个文件路径:

location /test {
     content_by_lua_file /path/test.lua;
 }

而且,如果这里不是绝对路径而是相对路径:

 content_by_lua_file path/test.lua;

那么就会使用 OpenResty 启动时指定的 -p 目录,来做一个拼接,从而得到绝对路径。

今天主要解答这几个问题。最后,欢迎你继续在留言区写下你的疑问,我会持续不断地解答。希望可以通过交流和答疑,帮你把所学转化为所得。也欢迎你把这篇文章转发出去,我们一起交流、一起进步。