Apache APISIX 架构浅析

大家从网上肯定看到过关于Apisix性能高的文章,那么到底是如何实现的呢?

本文是分析也是自己学习《OpenResty从入门到实战》及Apisix官方文档的一个笔记

简单分析

先看一下官网的架构图

从图中可以看到APISIX是基于OpenResty与Nginx实现的。

这里需要注意OpenResty并不是Nginx的fork,也不是在Nginx的基础上加了一些常用库重新打包,而只是把Nginx当作底层的网络库来使用。

那具体是如何使用Nginx来作为网络库的呢?

其实很简单就是在Nginx.conf中做简单的配置,让所有的流量都通过网关的Lua代码来处理。

注:Nginx默认不支持Lua的,这部分是OpenResty的能力

复制代码
server {
    listen 9080;

    init_worker_by_lua_block {
        apisix.http_init_worker()
    }

    location / {
        access_by_lua_block {
            apisix.http_access_phase()
        }
        header_filter_by_lua_block {
            apisix.http_header_filter_phase()
        }
        body_filter_by_lua_block {
            apisix.http_body_filter_phase()
        }
        log_by_lua_block {
            apisix.http_log_phase()
        }
    }
}
# 这里只是一个简单的示意,完整的配置可以看到文末的nginx.conf文件

在这个示例中,监听了 9080 端口,并通过location /的方式,把这个端口的所有请求都拦截下来,并依次通过accessrewriteheader filterbody filterlog这几个阶段进行处理,在每个阶段中都会去调用对应的插件函数。其中,rewrite阶段便是在apisix.http_access_phase函数中合并处理的。

这里是一份完整的Nginx的配置文件:APISIX Nginx.conf

这里补充下OpenResty Phases

那Lua层面是如何保证高性能的呢?

协程+事件

在OpenResty层面,Lua的协程会与Nginx的事件机制相互配合。如果Lua代码中出现类似查询 MySQL 数据库这样的 I/O 操作,就会先调用Lua协程的 yield 把自己挂起,然后在Nginx中注册回调;在 I/O 操作完成(也可能是超时或者出错)后,再由Nginx回调 resume 来唤醒Lua协程。这样就完成了Lua协程和Nginx事件驱动的配合,避免在Lua代码中写回调。

LuaJIT

我们先来看下LuaJIT在OpenResty整体架构中的位置:

OpenResty的worker进程都是fork master进程而得到的,其实,master进程中的LuaJIT虚拟机也会一起fork过来。在同一个worker内的所有协程,都会共享这个LuaJIT虚拟机,Lua代码的执行也是在这个虚拟机中完成的。

标准 Lua 和 LuaJIT

其实标准Lua出于性能考虑,也内置了虚拟机,所以Lua代码并不是直接被解释执行的,而是先由Lua编译器编译为字节码(Byte Code),然后再由Lua虚拟机执行。

而LuaJIT的运行时环境,除了一个汇编实现的Lua解释器外,还有一个可以直接生成机器代码的JIT编译器。开始的时候,LuaJIT和标准Lua一样,Lua代码被编译为字节码,字节码被LuaJIT的解释器解释执行。

但不同的是,LuaJIT的解释器会在执行字节码的同时,记录一些运行时的统计信息,比如每个Lua函数调用入口的实际运行次数,还有每个Lua循环的实际执行次数。当这些次数超过某个随机的阈值时,便认为对应的Lua函数入口或者对应的Lua循环足够热,这时便会触发JIT编译器开始工作。

JIT 编译器会从热函数的入口或者热循环的某个位置开始,尝试编译对应的Lua代码路径。编译的过程,是把LuaJIT字节码先转换成LuaJIT 自己定义的中间码(IR),然后再生成针对目标体系结构的机器码。

所以,所谓LuaJIT的性能优化,本质上就是让尽可能多的Lua代码可以被JIT编译器生成机器码,而不是回退到Lua解释器的解释执行模式

注意

  • LuaJIT并不完备,存在NYI(Not Yet Implemented)问题
  • LuaJIT 的作者目前处于半退休状态
  • OpenResty用的LuaJIT是LuaJIT自己维护的分支

语句摘录

在学习《OpenResty从入门到实战》的过程中对于一下几句话深有感触,这里摘录一下与大家共勉:

  • 在我看来,明白一个技术为何存在,并弄清楚它和别的类似技术之间的差异和优势,远比你只会熟练调用它提供的 API 更为重要。这种技术视野,会给你带来一定程度的远见和洞察力,这也可以说是工程师和架构师的一个重要区别。
  • 任何技术课程的学习,都不能代替对官方文档的仔细研读。这些耗时的笨功夫,每个人都省不掉的。
  • OpenResty 现在的官方文档只有英文版本,国内工程师在阅读时,难免会因为语言问题,抓不住重点,甚至误解其中的内容。但越是这样,越没有捷径可走,你更应该仔细地把文档从头到尾读完,并在有疑问时,结合测试案例集和自己的尝试,去确定出答案。这才是辅助我们学习OpenResty的正确途径。
  • 对待技术的选择,我们可以有倾向,但还是不要一概而论绝对化,因为并没有一个可以适合所有缓存场景的银弹。根据实际场景的需要,构建一个最小化可用的方案,然后逐步地增加,是一个不错的法子。

OpenResty vs Envoy 底座比对

随着云原生的普及,Kubernetes技术栈的南北向流量网关也开始普及:南北入口网关选型

虽然从目前的南北向网关功能来看还不如以OpenResty为底座的Kong或者APISIX,但云原生社区明显更加活跃。

下面从Contributors、Star、Commits、Releases四方面来分析比对(数据取值时间:2025.03.22):

从目前功能来看Kong或者APISIX能力更全,但从长远来看,个人更看好Kubernetes Gateway API相关的网关。

现在感觉Cloudflare也放弃了OpenResty,转到了Pingora:how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet

Pingora 并不是一个工具,而是一个库。

相关推荐
用户10005229303937 分钟前
【设计模式】深入解析装饰器模式(Decorator Pattern)
面试·架构
CopyLower2 小时前
Apache Dubbo 与 ZooKeeper 集成:服务注册与发现的全解析
zookeeper·apache·dubbo
哔哩哔哩技术2 小时前
2025 B站春晚直播——极速流式直转点在春晚项目中的实践
架构
SimonKing3 小时前
XXL-JOB:揭秘定时机制
java·后端·架构
ZZDICT3 小时前
OpenResty(Lua)+Redis实现动态封禁IP
redis·nginx·lua·openresty
Hello.Reader4 小时前
Apache Dubbo Pixiu打造微服务生态的轻量级 API 网关
微服务·apache·dubbo
x-cmd4 小时前
[250324] Kafka 4.0.0 版本发布:告别 ZooKeeper,拥抱 KRaft!| Wine 10.4 发布!
java·分布式·zookeeper·kafka·apache·kraft·wine
故事与他6455 小时前
Apache中间件漏洞攻略
java·服务器·安全·网络安全·中间件·log4j·apache
编程在手天下我有5 小时前
三分钟读懂微服务
微服务·云原生·架构
Data-Miner6 小时前
122页满分PPT | 企业数字化转型4A架构企业数字化转型架构企业架构EA解决方案咨询案例
架构