01|初探OpenResty的三大特性
文章目录
你好,我是温铭。
开篇词里我们说过,OpenResty 的优势显而易见。不过,在具体学习之前,让我们先简单回顾下 OpenResty 的发展过程,这有助于你对后面内容有更好的理解。
OpenResty 的发展
OpenResty 并不像其他的开发语言一样从零开始搭建,而是基于成熟的开源组件——NGINX 和 LuaJIT。OpenResty 诞生于 2007 年,不过,它的第一个版本并没有选择 Lua,而是用了 Perl,这跟作者章亦春的技术偏好有很大关系。
但 Perl 的性能远远不能达到要求,于是,在第二个版本中,Perl 就被 Lua 给替换了。不过,在 OpenResty 官方的项目中,Perl 依然占据着重要的角色,OpenResty 工程化方面都是用 Perl 来构建,比如测试框架、Linter、CLI 等,后面我们也会逐步介绍。
后来,章亦春离开了淘宝,加入了美国的 CDN 公司 Cloudflare。因为 OpenResty 高性能和动态的优势很适合 CDN 的业务需求,很快,OpenResty 就成为 CDN 的技术标准。通过丰富的 lua-resty 库,OpenResty 开始逐渐摆脱 NGINX 的影子,形成自己的生态体系,在 API 网关、软 WAF 等领域被广泛使用。
其实,我经常说,OpenResty 是一个被广泛使用的技术,但它并不能算得上是热门技术,这听上去有点矛盾,到底什么意思呢?
说它应用广,是因为 OpenResty 现在是全球排名第五的 Web 服务器。我们经常用到的 12306 的余票查询功能,或者是京东的商品详情页,这些高流量的背后,其实都是 OpenResty 在默默地提供服务。
说它并不热门,那是因为使用 OpenResty 来构建业务系统的比例并不高。使用者大都用 OpenResty 来处理入口流量,并没有深入到业务里面去,自然,对于 OpenResty 的使用也是浅尝辄止,满足当前的需求就可以了。这当然也与 OpenResty 没有像 Java、Python 那样有成熟的 Web 框架和生态有关。
说了这么多,接下来,我重点来介绍下,OpenResty 这个开源项目值得称道和学习的几个地方。
OpenResty 的三大特性
详尽的文档和测试用例
没错,文档和测试是判断开源项目是否靠谱的关键指标,甚至是排在代码质量和性能之前的。
OpenResty 的文档非常详细,作者把每一个需要注意的点都写在了文档中。绝大部分时候,我们只需要仔细查看文档,就能解决遇到的问题,而不用谷歌搜索或者是跟踪到源码中。为了方便起见,OpenResty 还自带了一个命令行工具restydoc
,专门用来帮助你通过 shell 查看文档,避免编码过程被打断。
不过,文档中只会有一两个通用的代码片段,并没有完整和复杂的示例,到哪里可以找到这样的例子呢?
对于 OpenResty 来说,自然是/t
目录,它里面就是所有的测试案例。每一个测试案例都包含完整的 NGINX 配置和 Lua 代码,以及测试的输入数据和预期的输出数据。不过,OpenResty 使用的测试框架,与其他断言风格的测试框架完全不同,后面我会用专门章节来做介绍。
同步非阻塞
协程,是很多脚本语言为了提升性能,在近几年新增的特性。但它们实现得并不完美,有些是语法糖,有些还需要显式的关键字声明。
OpenResty 则没有历史包袱,在诞生之初就支持了协程,并基于此实现了同步非阻塞的编程模式。这一点是很重要的,毕竟,程序员也是人,代码应该更符合人的思维习惯。显式的回调和异步关键字会打断思路,也给调试带来了困难。
这里我解释一下,什么是同步非阻塞。先说同步,这个很简单,就是按照代码来顺序执行。比如下面这段伪码:
|
|
在同一请求连接中,如果要等 MySQL 的查询结果返回后,才能继续去查询 Redis,那就是同步;如果不用等 MySQL 的返回,就能继续往下走,去查询 Redis,那就是异步。对于 OpenResty 来说,绝大部分都是同步操作,只有 ngx.timer
这种后台定时器相关的 API,才是异步操作。
再来说说非阻塞,这是一个很容易和“异步”混淆的概念。这里我们说的“阻塞”,特指阻塞操作系统线程。我们继续看上面的例子,假设查询 MySQL 需要 1s 的时间,如果在这 1s 内,操作系统的资源(CPU)是空闲着并傻傻地等待返回,那就是阻塞;如果 CPU 趁机去处理其他连接的请求,那就是非阻塞。非阻塞也是 C10K、C100K 这些高并发能够实现的关键。
同步非阻塞这个概念很重要,建议你仔细琢磨一下。我认为,这一概念最好不要通过类比来理解,因为不恰当的类比,很可能把你搞得更糊涂。
在 OpenResty 中,上面的伪码就可以直接实现同步非阻塞,而不用任何显式的关键字。这里也再次体现了,让开发者用起来更简单,是 OpenResty 的理念之一。
动态
OpenResty 有一个非常大的优势,并且还没有被充分挖掘,就是它的动态。
传统的 Web 服务器,比如 NGINX,如果发生任何的变动,都需要你去修改磁盘上的配置文件,然后重新加载才能生效,这也是因为它们并没有提供 API,来控制运行时的行为。所以,在需要频繁变动的微服务领域,NGINX 虽然有多次尝试,但毫无建树。而异军突起的 Envoy,正是凭着 xDS 这种动态控制的 API,大有对 NGINX 造成降维攻击的威胁。
和 NGINX、Envoy 不同的是,OpenResty 是由脚本语言 Lua 来控制逻辑的,而动态,便是 Lua 天生的优势。通过 OpenResty 中 lua-nginx-module 模块中提供的 Lua API,我们可以动态地控制路由、上游、SSL 证书、请求、响应等。甚至更进一步,你可以在不重启 OpenResty 的前提下,修改业务的处理逻辑,并不局限于 OpenResty 提供的 Lua API。
这里有一个很合适的类比,可以帮你理解上面关于动态的说明。你可以把 Web 服务器当做是一个正在高速公路上飞驰的汽车,NGINX 需要停车才能更换轮胎,更换车漆颜色;Envoy 可以一边跑一边换轮胎和颜色;而 OpenResty 除了具备前者能力外,还可以在不停车的情况下,直接把汽车从 SUV 变成跑车。
显然,掌握这种“逆天”的能力后,OpenResty 的能力圈和想象力就扩展到了其他领域,比如 Serverless 和边缘计算等。
你学习的重点在哪里?
讲了这么多 OpenResty 的重点特性,你又该怎么学呢?我认为,学习需要抓重点,围绕主线来展开,而不是眉毛胡子一把抓,这样,你才能构建出脉络清晰的知识体系。
要知道,不管多么全面的课程,都不可能覆盖所有问题,更不能直接帮你解决线上的每个 bug 和异常。
回到 OpenResty 的学习,在我看来,想要学好 OpenResty,你必须理解下面 8 个重点:
- 同步非阻塞的编程模式;
- 不同阶段的作用;
- LuaJIT 和 Lua 的不同之处;
- OpenResty API 和周边库;
- 协程和 cosocket;
- 单元测试框架和性能测试工具;
- 火焰图和周边工具链;
- 性能优化。
这些内容正是我们学习的重点,在专栏的各个模块中我都会分别讲到。在学习的过程中,我希望你能举一反三,并且根据自己的兴趣点和背景,有针对性地深入阅读某些章节。
如果你是 OpenResty 的初学者,那么你可以完全跟着专栏的进度,在自己的环境中安装 OpenResty,运行并修改示例代码。要记住,你的重点在于构建 OpenResty 的全貌,而非死磕某个知识点。当然,如果你有疑问的地方,随时可以在留言区提出,我会解答你的困惑。
如果你正在项目中使用 OpenResty,那就太棒了,相信你在阅读 LuaJIT 和性能优化章节时,一定会有更多的共鸣,更能应用到实际,在你的项目中看到优化前后的性能指标变化。
另外,如果你想要给 OpenResty 以及周边库贡献代码,那么最大的门槛,并不是对 OpenResty 原理的理解,或者是如何编写 NGINX C 模块的问题,而是测试案例和代码规范。我见过太多 OpenResty 的代码贡献者(也包括我自己),在一个 PR 上反复修改测试案例和代码风格,这其中有太多鲜为人知的潜规则。所以,专栏的代码规范和单元测试部分,就是为你准备的。
而如果你是测试工程师,即使你不使用 OpenResty,OpenResty 的测试框架和性能分析工具集,也必能给你非常多的启发。毕竟,OpenResty 在测试上面的投入和积累是相当深厚的。
写在最后
欢迎你留言和我分享你的 OpenResty 学习之路,在这期间,你又走过哪些弯路呢?也欢迎你把这篇文章转发给你的同事、朋友。
还是那句话,在学习的过程中,你有任何疑问,都可以在专栏中留言,我会第一时间给你答复。
文章作者 anonymous
上次更新 2024-05-20