文章来自于对极客空间《OpenResty 从入门到实战》课程的学习汇总。
阻塞函数
一个重要原则——避免使用阻塞函数
OpenResty 的高性能的由来,主要是因为 “使用” 了 NGINX 的事件处理和 Lua 的协程机制。如果我 们使用阻塞函数的话,就意味着会有阻塞这一 ”使用“ 的可能性。这就会导致请求无法正常交由 NGINX 和 Lua 进行处理。
在调用外部命令时需要注意的事情
Lua 使用 os.execute
函数调用外部命令,如果调用时间需要几百毫秒~几秒钟,那么我们会遇到阻塞,性能会下降!
同样是会有遇到阻塞情况的可能性,我们需要想办法避免阻塞。原课程中,主讲人提出几个实际的场景例子:拷贝文件、使用 OpenSSL 生成密钥等耗时久的操作。针对这些场景,提出了两个具体的方案 FFI 库调用、ngx.pipe 的 lua-resty-shell 库的使用。
当然本质上还是没有离开主题——“避免使用阻塞函数”
磁盘 I/O
个人理解,磁盘读写向来都是最损耗性能的,我们首先应该要做的就是减少 I/O 读写。
这边我学习到的是,可以通过使用 lua-io-nginx-module
这个第三方的 C 模块提高性能。原理是将 I/O 操作从主线程转移到另外一个线程中处理,降低线程损耗。
这边关于主线程和子线程,我的理解是,主线程意味着是子线程的守门员,但是主线程也有自己跑路的风险——即提前下线,如果主线程依靠 pthread_exit 提前下线,而不是 return,那么其他线程就会继续运行。会变成僵死的状态。这就会对性能造成很大的影响。
luasocket
这边引用作者的原话吧。感觉作者的原话简洁明了
我们来说说 luasocket ,它也是容易被开发者用到的一个 Lua 内置库,经常有人分不清 luasocket 和 OpenResty 提供的 cosocket。luasocket 也可以完成网络通信的功能,但它并没有非阻塞的优势。如果你使用了 luasocket,那么性能也会急剧下降。
字符串
对我有用的一点:在 lua 中,字符串是不可变的
即当你修改一个字符串的值时,其实做的操作是创建了一个新的字符串对象,只不过修改了数据的引用罢了。
Table
优化原则
- 尽量复用,避免不必要的 table 的创建
- 预先生成数组
- 手动计算下标值
- 循环使用单个 table
- 使用缓存——table 池
调优手段
明确一下,本部分内容主要是 OpenResty 的调试
断电+打日志
断点是必须掌握的
这边有一个经验得记下
两个前提:测试环境,以及稳定复现。
然后有一个工具推荐 Mozilla RR。
二分查找+注释
额,二分我自己就很常用啊
动态调试
Dtrace——动态调试!这个是宝藏,我得玩一下去,这边也直接引用作者的语言描述
我就曾经遇到过这样的一个 bug。多年前,我负责的一个系统在每天凌晨 1 点钟左右时,数据库资源就会被耗尽,并导致整个系统雪崩。当时,我们白天排查代码中的计划任务,到了晚上,团队的同学们就蹲守在公司等 bug 复现,复现的时候再去查看各自子模块的运行状态。这样下来,直到第三个晚上才找到了 bug 的元凶。我的这个经历,和 Solaris 几个系统工程师创造 Dtrace 的背景很类似。当时 Solaris 的工程师们,也是花了几天几夜的时间排查一个诡异的线上问题,最后才发现是因为一个配置写错了。但和我不同的是,Solaris 的工程师决定彻底避免这种问题,于是发明了 Dtrace,专门用于动态调试。动态调试,也叫做活体调试。和 GDB 这种静态调试工具不同,动态调试可以调试线上的服务,而对调试的程序而言,整个调试过程是无感知、无侵入的,不用你修改代码,更不用重启。打一个比方,动态调试就像 X 光,可以在病人无感知的情况下检查身体,而不需要抽血和胃镜。Dtrace 便是最早的动态追踪框架,受到它的影响,其他系统中也逐渐出现了类似的动态调试工具。比如,Red Hat 的工程师,就在 Linux 平台上创造了 Systemtap,也就是我接下来要讲的主角。
有助于我更快理解 Dtrace 是什么。
Systemtap
※这个对我来说也是新鲜事物,可以学习!
其他的动态追踪框架
跟不上了跟不上了,引用作者原话吧,方便我日后理解呢
当然,对于内核和性能分析工程师来说,只有 Systemtap 还是不够用的。首先, Systemtap 并没有默认进入系统内核;其次,它的工作原理决定了它的启动速度比较慢,而且有可能对系统的正常运行造成影响。eBPF(extended BPF)则是最近几年 Linux 内核中新增的特性。相比 Systemtap,eBPF 有内核直接支持、不会死机、启动速度快等优点;同时,它并没有使用 DSL,而是直接使用了 C 语言的语法,所以也大大降低了它的上手难度。除了开源的解决方案外,Intel 出品的 VTune 也是神兵利器之一。它直观的界面操作和数据展示,可以让你不写代码也能分析出性能的瓶颈。
定位性能瓶颈
wrk 和火焰图
缓存
缓存两原则:
一是越靠近用户的请求越好。比如,能用本地缓存的就不要发送 HTTP 请求,能用 CDN 缓存的就不要打到源站,能用 OpenResty 缓存的就不要打到数据库。二是尽量使用本进程和本机的缓存解决。因为跨了进程和机器甚至机房,缓存的网络开销就会非常大,这一点在高并发的时候会非常明显。
shared dict 缓存和 lru 缓存
缓存风暴
解决:lua--resty-* 封装
突发流量
接下来的内容就全是概念性的东西了,google 一下你就知道。