极客时间已完结课程限时免费阅读

01 | 初探OpenResty的三大特性

01 | 初探OpenResty的三大特性-极客时间

01 | 初探OpenResty的三大特性

讲述:温铭

时长09:59大小9.12M

你好,我是温铭。
开篇词里我们说过,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 则没有历史包袱,在诞生之初就支持了协程,并基于此实现了同步非阻塞的编程模式。这一点是很重要的,毕竟,程序员也是人,代码应该更符合人的思维习惯。显式的回调和异步关键字会打断思路,也给调试带来了困难。
这里我解释一下,什么是同步非阻塞。先说同步,这个很简单,就是按照代码来顺序执行。比如下面这段伪码:
local res, err = query-mysql(sql)
local value, err = query-redis(key)
在同一请求连接中,如果要等 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 学习之路,在这期间,你又走过哪些弯路呢?也欢迎你把这篇文章转发给你的同事、朋友。
还是那句话,在学习的过程中,你有任何疑问,都可以在专栏中留言,我会第一时间给你答复。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 23

提建议

上一篇
开篇词 | OpenResty,为你打开高性能开发的大门
下一篇
02 | 如何写出你的“hello world”?
unpreview
 写留言

精选留言(30)

  • 行者
    2019-05-27
    同步异步说的是代码,调用就有返回是同步,反之是异步。 阻塞非阻塞说的是cpu,apu要等待就是阻塞,反之非阻塞。 非阻塞并不能缩减rt时间,其最大的优点是可以服务更多的请求,达到c100k。 针对ff同学的问题,阻塞非阻塞对于代码来说是,仅仅是底层实现不同。
    展开
    41
  • zhang
    2019-05-27
    分享一篇以前学openresty 时写的笔记,当时处于一个阅读过nginx源码,但是没有实际使用或者开发nginx的情况,另外个人的描述描述能力也比较差,很多知识储备不足。 当时写这篇笔记并不是对源码进行解读,只是站在一个有什么功能,我应该如何实现它,它是如何做的,这样一个角度去分析的。 希望这篇笔记可以让大家有一定收获,也希望我们可以互相扶持,一起坚持下去,学好这门课程。 http://note.youdao.com/noteshare?id=965c9f034a82ffb0f8b4de6ca81f3e73
    展开

    作者回复: 👍 一起学习

    15
  • 一步
    2019-05-27
    看了下 openresty github 仓库 https://github.com/openresty/openresty 发现 t文件夹下没有什么测试文件,这个是需要看每个相关的模块的仓库吗? 又看了 https://github.com/openresty/lua-nginx-module 这个模块发现 t文件夹下是有测试文件的

    作者回复: https://github.com/openresty/openresty 是用于打源码包的项目,所以测试案例不多。 是的,需要看这个子项目的仓库。

    7
  • FF
    2019-05-27
    请教下温老师,关于阻塞/非阻塞。 如果 CPU 趁机去处理其他连接的请求,那就是非阻塞。 但对于用户线程来讲,怎么理解这个非阻塞呢? 理解1,这个查询的用户线程是不是还得阻塞等待 1 秒钟等待返回?这样的话应用的性能还是会不理想? 理解2,用户线程也是非阻塞,操作系统线程非阻塞返回后,用户的数据不一定有,这个时候用户线程要轮询去调用查询,直到有数据。这样的话,对于应用来讲,性能不是一样不理想? 哪种理解是对的呢?但无论哪种,用户应用性能可能都提不上理想,这样的话为何非阻塞是C10K,C100K 实现的关键呢?
    展开

    作者回复: 理解 1 是对的。站在用户请求的角度,非阻塞并不会减少处理的时间,但是会减少等待的时间。OpenResty 的每个 worker 同一时间只在处理一个请求,如果阻塞了,这个 worker 上的其他请求都需要等待。 C10K 要解决的是高并发的问题,是服务端的整体性能。

    5
  • 业余草
    2019-05-29
    总结:OpenResty 的三大特性。 1、详尽的文档和测试用例(这个不能算特性吧)。 2、同步非阻塞。 3、动态。
    4
  • Luciano李鑫
    2019-06-04
    pr是啥意思

    作者回复: 是 GitHub 中 Pull Request 的缩写

    3
  • shonm
    2019-06-02
    老师你好,上面的代码中,如果是非阻塞的,他不是立马返回吗,怎么又会等1秒,怎么做到同步呢?

    作者回复: 非阻塞的自然不会等待 1 秒,但这 1 秒钟的时间内,CPU 是去处理其他请求的逻辑,并且把当前请求挂起。 等数据库返回了结果后,才唤醒之前的请求,这样就做到了同步。

    共 3 条评论
    4
  • 馬偉偉💫
    2019-05-27
    期待已久,第一次听说这技术就是在网易云课堂老师讲的课,买了书准备学习老师就开了极客时间的专栏,结合书集和老师的专栏希望能有所收获。

    作者回复: 一起学习

    3
  • 阳仔
    2019-06-03
    Lua到底怎么读?

    作者回复: 我读“路啦”

    共 2 条评论
    2
  • LoveDr.kang
    2019-05-28
    不要通过类比去理解同步非阻塞,从这句话就感觉老师很务实,网上很多举例子的真的特别误导人,尤其一些没实际经验的新手,还是要从概念入手,多多体会,才能领会精髓。
    3
  • 许多子
    2019-05-28
    请问openresty可不可以就当作nginx来使用呢?不写lua的情况下,用来搭建web服务器,这两者有没有区别呢?

    作者回复: 当然可以,OpenResty 是基于 NGINX 的。但需要注意的是,OpenResty 的版本一般会落后于 NGINX。

    2
  • 小刚
    2019-05-27
    代码实现是同步与异步,阻塞是线程调用过程
    1
  • 舟 leo
    2022-03-23
    老师 有没有OpenResty的微信交流群呀
  • lzh
    2021-08-08
    阻塞和非阻塞那里,我觉得文中有点没讲清楚。结合APUE第14章和自己的理解补充一下,仅供参考。 阻塞,意味着这个进程会被挂起;非阻塞,如果IO能读,就返回数据,err=nil;不能读则返回err != nil。 这里说mysql操作要1s(这个1s内,对mysql请求都会返回err!=nil),然后CPU趁机去处理其他请求,openresty在这里可能是用了类似yeild之类的方式(协程),当mysql返回err时,yeild到其他函数中执行,进程一直都在跑,没有被挂起。 如果当前进程就只有操作mysql一个协程(简单理解就是进程只有这一个专门query mysql的函数要执行),那这1s就会反复yeild出去然后又进入这个query函数,相当于一直在query mysql,然后mysq一直返回err。这样就是非阻塞的,在mysql没有返回数据前,进程是不会query redis的,这样就是同步的。 之前乍看“cpu趁机处理其他请求”这句话,会让我以为进程被挂起,这就感觉与非阻塞矛盾,像这种协程的方式,如果能按yeild来说说,那瞬间就能理解了。
    展开
  • 贺钧威
    2021-07-24
    老师好,想问下 nginx 是异步非阻塞的,openresty是同步非阻塞的,那他们之间的同步异步的差异具体是指什么
  • Geek_b2b3f5
    2020-04-25
    老师,我刚入门OR, 有个小问题请教一下,ngx.utctime()与系统时间一致,ngx.localtime()比系统时间+8小时,怎么才能设置正常呢?
    共 1 条评论
  • Geek__CherryKing
    2020-03-27
    初学入坑打卡
  • lcp0578
    2020-03-21
    不错👍
  • jackstraw
    2020-02-19
    老师有遇到在高并发的情况下,lua代码不执行的情况么?
  • Geek_67aae8
    2019-12-11
    做爬虫的话,怎么样?