亿级流量网关设计:从Nginx到OpenResty再到APISIX的演进之路

发布时间:2026/7/9 2:07:30

亿级流量网关设计:从Nginx到OpenResty再到APISIX的演进之路 亿级流量网关设计从Nginx到OpenResty再到APISIX的演进之路一、引言网关演进的必然性网关是微服务架构的入口承担着路由转发、限流熔断、认证鉴权、协议转换等核心职责。在流量规模从百万级到亿级的跨越中网关的架构选择直接决定了系统的可用性上限。团队在2018年以 Nginx 起步峰值 QPS 达到 3 万时发现了明显瓶颈2019年迁移到 OpenResty Lua将 QPS 推到 15 万2022年全面切换到 Apache APISIX当前稳定支撑单集群 50 万 QPS。这不是一个简单的换工具过程而是对网关架构本质的逐步理解从静态配置到动态路由从单体扩展到水平弹性从单一插件到生态化扩展。二、原理剖析三代网关的架构差异2.1 三代网关的核心架构对比graph TB subgraph 第一代Nginx N1[Nginx Master] -- N2[Nginx Worker × N] N2 -- N3[静态配置文件br/nginx.conf] N3 -- N4[变更需 reloadbr/可能丢连接] end subgraph 第二代OpenResty O1[Nginx LuaJIT] -- O2[Lua 脚本动态路由] O2 -- O3[共享内存br/lua-shared-dict] O3 -- O4[部分动态化br/需代码级修改] end subgraph 第三代APISIX A1[APISIX Corebr/etcd 驱动] -- A2[Admin APIbr/配置管理] A2 -- A3[etcd Clusterbr/配置中心] A3 -- A4[Plugin Runnerbr/多语言插件] A4 -- A5[完全动态br/毫秒级生效] end N4 -.-|演进| O4 O4 -.-|演进| A5 style N4 fill:#ef9a9a,stroke:#333 style O4 fill:#fff9c4,stroke:#333 style A5 fill:#a5d6a7,stroke:#3332.2 动态路由与插件链APISIX 的请求处理采用插件链模式每个插件在特定阶段介入graph LR R[请求到达] -- S1[rewritebr/URL重写/重定向] S1 -- S2[accessbr/认证/限流/黑白名单] S2 -- S3[before_proxybr/请求头改写] S3 -- S4[proxybr/上游转发] S4 -- S5[header_filterbr/响应头处理] S5 -- S6[body_filterbr/响应体处理] S6 -- S7[logbr/日志/指标上报] style S2 fill:#ff9800,stroke:#333 style S4 fill:#2196f3,stroke:#333,color:#fff核心优势插件按阶段独立执行互不阻塞新增插件不影响现有流程每个阶段可并行执行多个插件。三、生产级代码与配置3.1 APISIX 高性能路由配置# config.yaml —— APISIX 生产配置 apisix: node_listen: 9080 enable_admin: true enable_control: true enable_reuseport: true # SO_REUSEPORT减少锁竞争 enable_dev_mode: false enable_heartbeat: true router: http: radixtree_uri # 基数树路由O(K)复杂度 ssl: radixtree_sni nginx_config: worker_processes: auto # 自动匹配CPU核数 worker_rlimit_nofile: 65535 worker_connections: 32768 events: use: epoll multi_accept: on # 一次accept多个连接 http: lua_shared_dict: plugin-limit-conn: 10m plugin-limit-req: 10m plugin-limit-count: 10m prometheus-metrics: 32m # 连接池优化 upstream: keepalive: 320 # 每个worker的keepalive连接池 keepalive_requests: 1000 keepalive_timeout: 60s # etcd 配置 —— 配置中心 etcd: host: - http://etcd-1.prod:2379 - http://etcd-2.prod:2379 - http://etcd-3.prod:2379 prefix: /apisix timeout: 30 resync_delay: 5 # 配置同步间隔秒3.2 自定义限流插件Lua-- plugins/sliding-window-limiter.lua -- 滑动窗口限流比固定窗口更平滑避免边界突发 local core require(apisix.core) local redis require(resty.redis) local plugin_name sliding-window-limiter local schema { type object, properties { count { type integer, minimum 1 }, time_window { type integer, minimum 1 }, -- 秒 key { type string, default remote_addr }, redis_host { type string }, redis_port { type integer, default 6379 }, }, required { count, time_window, redis_host }, } local _M { version 0.1, priority 2900, name plugin_name, schema schema, } -- 滑动窗口Redis Lua脚本原子操作 local SLIDING_WINDOW_SCRIPT [[ local key KEYS[1] local window tonumber(ARGV[1]) -- 窗口大小秒 local limit tonumber(ARGV[2]) -- 限制次数 local now tonumber(ARGV[3]) -- 当前时间戳毫秒 -- 移除窗口外的记录 redis.call(ZREMRANGEBYSCORE, key, 0, now - window * 1000) -- 当前窗口内的请求数 local current redis.call(ZCARD, key) if current limit then redis.call(ZADD, key, now, now .. - .. math.random()) redis.call(EXPIRE, key, window 1) return 1 -- 允许 else return 0 -- 拒绝 end ]] function _M.check_schema(conf) return core.schema.check(schema, conf) end function _M.access(conf, ctx) local key if conf.key remote_addr then key ctx.var.remote_addr elseif conf.key http_x_user_id then key core.request.header(ctx, X-User-Id) end if not key then return 403, { message Missing rate limit key } end local red redis:new() red:set_timeout(conf.timeout or 100) local ok, err red:connect(conf.redis_host, conf.redis_port) if not ok then core.log.error(redis connect failed: , err) return -- 降级Redis不可用时放行 end local now ngx.now() * 1000 local res, err red:eval(SLIDING_WINDOW_SCRIPT, 1, rate_limit: .. key, conf.time_window, conf.count, now) if res 0 then return 429, { message Too many requests } end end return _M3.3 大规模集群灰度发布# APISIX Route —— 按Header灰度 routes: - id: order-service-canary uri: /api/v1/orders/* upstream_id: order-stable vars: - [http_x_canary, , v2] upstream: type: roundrobin nodes: order-canary-1.prod:8080: 1 order-canary-2.prod:8080: 1 - id: order-service-stable uri: /api/v1/orders/* upstream_id: order-stable priority: 0 # 低优先级兜底 upstream: type: roundrobin nodes: order-stable-1.prod:8080: 1 order-stable-2.prod:8080: 1四、边界条件与选型决策4.1 三者的性能基准对比指标Nginx 1.24OpenResty 1.21APISIX 3.8空路由 QPS45万38万32万带10个插件 QPS不可达22万18万配置热更新需reload部分支持毫秒级插件开发成本C模块Lua脚本Lua/Go/Wasm动态路由不支持Lua实现原生支持集群配置管理无无etcd原生APISIX 在纯转发场景下性能略低于 Nginx但差距约 30%换来的是动态路由、热更新、多语言插件等工程能力——对于亿级流量场景这个 trade-off 是值得的。4.2 何时不需要升级以下场景不需要从 Nginx 升级到 APISIX路由规则不超过 20 条且极少变更不需要动态限流/认证等高级插件团队无 Lua 或 Go 开发能力QPS 低于 5000简单架构已足够技术选型的核心原则是匹配当前复杂度而非盲目追求先进。4.3 etcd 的稳定性是 APISIX 的阿喀琉斯之踵APISIX 依赖 etcd 做配置中心etcd 集群的健康直接影响 APISIX 的路由更新能力。生产环境必须etcd 集群至少 3 节点APISIX 侧配置resync_delay做定期全量同步兜底监控 etcd 的etcd_server_has_leader和etcd_server_proposals_committed_total指标APISIX 内存中有完整路由缓存etcd 宕机不影响已加载路由的转发五、总结从 Nginx → OpenResty → APISIX 的演进本质上是从**配置即代码到配置即数据**的范式转变。Nginx 时代路由规则是静态文件变更需要 reloadOpenResty 时代Lua 脚本赋予了部分动态能力但路由逻辑与网关代码耦合APISIX 时代路由规则成为 etcd 中的数据条目Admin API 赋予了对这些数据的声明式管理能力网关本身回归到纯粹的规则执行引擎定位。这个演进的背后是分布式系统设计的核心原则控制面与数据面的分离。当路由规则可以像数据一样被增删改查时网关的运维从配置管理变成了数据管理团队可以构建 CI/CD 流水线自动下发路由规则实现真正的 GitOps 网关管理。对于还在用 Nginx 做微服务网关的团队建议的迁移路径是先在非关键链路试水 OpenResty → 验证动态路由的价值 → 再评估是否全面切换到 APISIX。一步到位的风险远大于渐进式演进。

相关新闻