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 并不是一个工具,而是一个库。

相关推荐
美狐美颜sdk1 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
小雷FansUnion4 小时前
深入理解MCP架构:智能服务编排、上下文管理与动态路由实战
人工智能·架构·大模型·mcp
Fireworkitte4 小时前
Apache POI 详解 - Java 操作 Excel/Word/PPT
java·apache·excel
慌糖5 小时前
微服务介绍
微服务·云原生·架构
June bug5 小时前
【软考中级·软件评测师】下午题·面向对象测试之架构考点全析:分层、分布式、微内核与事件驱动
经验分享·分布式·职场和发展·架构·学习方法·测试·软考
森焱森8 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
蚂蚁数据AntData9 小时前
从性能优化赛到社区Committer,走进赵宇捷在Apache Fory的成长之路
大数据·开源·apache·数据库架构
小湘西9 小时前
Apache HttpClient 的请求模型和 I/O 类型
java·http·apache
go546315846510 小时前
修改Spatial-MLLM项目,使其专注于无人机航拍视频的空间理解
人工智能·算法·机器学习·架构·音视频·无人机
凌辰揽月11 小时前
8分钟讲完 Tomcat架构及工作原理
java·架构·tomcat