OpenResty
OpenResty
零、从B站崩溃说起
前不久B站挂了迅速登上全网热搜,问题在于B站使用的作为服务发现模块的OpenResty[1]中的Lua[2]脚本发生了算术运算错误产生死循环导致。而据参考文中[3]介绍
B站在19年9月份从Tengine迁移到了OpenResty,基于其丰富的Lua能力开发了一个服务发现模块,从我们自研的注册中心同步服务注册信息到Nginx共享内存中,SLB在请求转发时,通过Lua从共享内存中选择节点处理请求,用到了OpenResty的lua-resty-balancer模块。到发生故障时已稳定运行快两年时间。
其实,无论是B站还是360,UPYUN,阿里云,新浪,腾讯网,去哪儿网,酷狗音乐等都是 OpenResty 的深度用户。我们经常用到的 12306 的余票查询功能,或者是京东的商品详情页,这些高流量的背后,其实都是 OpenResty 在默默地提供服务。
一、初窥OpenResty
OpenResty(又称:ngx_openresty) 是一个基于 nginx的可伸缩的 Web 平台,由中国人章亦春发起,提供了很多高质量的第三方模块。它是一个强大的 Web 应用服务器,Web 开发人员可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,更主要的是在性能方面,OpenResty在诞生之初就支持了协程,并基于此实现了同步非阻塞的编程模式,可以快速构造出足以胜任 10K 以上并发连接响应的超高性能 Web 应用系统。
OpenResty 简单理解成封装了nginx,并且集成了LUA脚本,开发人员只需要简单的其提供了模块就可以实现相关的逻辑,而不再像之前,还需要在nginx中自己编写lua的脚本,再进行调用了。[5]
OpenResty 项目在如何科学和动态地调试代码上,花费了大量的精力,可以说是达到了极致。openresty-systemtap-toolkit 和 stapxx 这两个 OpenResty 的项目,都基于 systemtap 这个动态调试和追踪工具。使用 systemtap 最大的优势,便是实现活体分析,同时对目标程序完全无侵入。打个比方,systemtap,就像是我们去医院照了个 CT,无痛无感知。更棒的是,systemtap 可以生成直观的火焰图[6]来做性能分析,常见的火焰图类型有 On-CPU(cpu占用时间),Off-CPU(阻塞时间),还有 Memory(内存申请/释放函数调用次数),Hot/Cold(on-CPU和off-CPU结合),Differential 等等。
火焰图有以下特征(这里以 on-cpu 火焰图为例):
- 每一列代表一个调用栈,每一个格子代表一个函数
- 纵轴展示了栈的深度,按照调用关系从下到上排列。最顶上格子代表采样时,正在占用 cpu 的函数。
- 横轴的意义是指:火焰图将采集的多个调用栈信息,通过按字母横向排序的方式将众多信息聚合在一起。需要注意的是它并不代表时间。
- 横轴格子的宽度代表其在采样中出现频率,所以一个格子的宽度越大,说明它是瓶颈原因的可能性就越大。
- 火焰图格子的颜色是随机的暖色调,方便区分各个调用信息。
- 其他的采样方式也可以使用火焰图, on-cpu 火焰图横轴是指 cpu 占用时间,off-cpu 火焰图横轴则代表阻塞时间。
- 采样可以是单线程、多线程、多进程甚至是多 host
二、Nginx及Lua
传统的 Web 服务器,比如 NGINX,如果发生任何的变动,都需要你去修改磁盘上的配置文件,然后重新加载才能生效,这也是因为它们并没有提供 API,来控制运行时的行为。所以,在需要频繁变动的微服务领域,NGINX 虽然有多次尝试,但毫无建树。OpenResty 是由脚本语言 Lua 来控制逻辑的,而动态,便是 Lua 天生的优势。通过 OpenResty 中 lua-nginx-module 模块中提供的 Lua API,可以动态地控制路由、上游、SSL 证书、请求、响应等。甚至更进一步,可以在不重启 OpenResty 的前提下,修改业务的处理逻辑,并不局限于 OpenResty 提供的 Lua API。
1.Nginx
1. 在 OpenResty 的开发中,我们需要注意下面几点:
- 要尽可能少地配置 nginx.conf;
- 避免使用 if、set 、rewrite 等多个指令的配合;
- 能通过 Lua 代码解决的,就别用 NGINX 的配置、变量和模块来解决。
2. 每个指令都有自己适用的上下文(Context),也就是 NGINX 配置文件中指令的作用域。最上层的是 main,里面是和具体业务无关的一些指令,另外,上下文是有层级关系的,指令不能运行在错误的上下文中,NGINX 在启动时会检测 nginx.conf 是否合法。
3. NGINX 不仅可以处理 HTTP 请求 和 HTTPS 流量,还可以处理 UDP 和 TCP 流量。其中,七层的放在 HTTP 中,四层的放在 stream 中。在 OpenResty 里面, lua-nginx-module 和 stream-lua-nginx-module 分别和这俩对应。NGINX 支持的功能,OpenResty 并不一定支持,需要看 OpenResty 的版本号。OpenResty 的版本号是和 NGINX 保持一致的,所以很容易识别。
2.Lua
1. Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
2. 应用场景:
- 游戏开发(游戏外挂原理,有兴趣可以看看)
- 独立应用脚本
- Web 应用脚本
- 扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
- 安全系统,如入侵检测系统
- redis中嵌套调用实现类似事务的功能
- web容器中应用处理一些过滤 缓存等等的逻辑,例如nginx。
3. lua简单代码:运行环境
4. OpenResty 并没有直接使用 LuaJIT 官方提供的 2.1.0-beta3 版本,而是在此基础上,扩展了自己的 fork: [openresty-luajit2]。这些独有的 API,都是在实际开发 OpenResty 的过程中,出于性能方面的考虑而增加的。为什么不直接使用 Lua,而是要用自己维护的 LuaJIT 呢?其实,最主要的原因,还是 LuaJIT 的性能优势。所谓 LuaJIT 的性能优化,本质上就是让尽可能多的 Lua 代码可以被 JIT 编译器生成机器码,而不是回退到 Lua 解释器的解释执行模式。
三、应用
样例:广告缓存的载入和读取:
-
分析
首页门户系统需要展示各种各样的广告数据,针对这种变更频率低的数据,如何提升访问速度?
通常情况下,首页(门户系统的流量一般非常的高)不适合直接通过mysql数据库直接访问的方式来获取展示。
a. 首先访问nginx ,我们可以采用缓存的方式,先从nginx本地缓存中获取,获取到直接响应
b. 如果没有获取到,再次访问redis,我们可以从redis中获取数据,如果有 则返回,并缓存到nginx中
c. 如果没有获取到,再次访问mysql,我们从mysql中获取数据,再将数据存储到redis中,返回。
而这里面,我们都可以使用LUA脚本嵌入到程序中执行这些查询相关的业务。如图所示:
-
实现
(1)实现思路-查询数据放入redis中
定义请求:用于查询数据库中的数据更新到redis中。
a.连接mysql ,按照广告分类ID读取广告列表,转换为json字符串。
b.连接redis,将广告列表json字符串存入redis 。
请求: /update_content 参数: id --指定广告分类的id 返回值: json
请求地址:
<http://192.168.211.132/update_content?id=1>
创建/root/lua目录,在该目录下创建update_content.lua: 目的就是连接mysql 查询数据 并存储到redis中。
修改/usr/local/openresty/nginx/conf/nginx.conf文件: 添加头信息,和 location信息
定义lua缓存命名空间,修改nginx.conf,添加如下代码即可:
请求
<http://{ip}/update_content?id=1>
可以实现缓存的添加(2)实现思路-从redis中获取数据
实现思路:
定义请求,用户根据广告分类的ID 获取广告的列表。通过lua脚本直接从redis中获取数据即可。
请求:/read_content 参数:id 返回值:json
如果请求都到redis,redis压力也很大,所以我们一般采用多级缓存的方式来减少下游系统的服务压力。参考基本思路图的实现。
-
先查询openresty本地缓存 如果 没有
-
再查询redis中的数据,如果没有
-
再查询mysql中的数据,但凡有数据 则返回即可。
在/root/lua目录下创建read_content.lua:
测试地址:
http://{ip}/read_content?id=1
,此时会获取分类ID=1的所有广告信息。 -
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」