Skip to main content

百万级 QPS 业务新宠,金山办公携手 Apache APISIX 打造网关实践新体验

· One min read

本文由金山办公中台部门 SRE 网络负责人张强介绍了金山办公如何使用 Apache APISIX 应对百万级 QPS 业务,同时基于 Apache APISIX 更新与改进网关实践层面的内容。

背景介绍#

金山办公是目前国内最大的办公软件厂商,旗下产品涉及 WPS、金山文档、稻壳等。在业务层面上由数千个业务以容器化部署在内部云原生平台,目前 Apache APISIX 在金山办公主要负责为中台部门业务(百万级 QPS )提供相关网关服务。

金山办公的网关演进#

在 1.0 阶段时,我们对于 API Gateway 的特性没有什么强需求,只是想解决运维问题,所以基于 OpenResty 与 Lua 进行了自研,实现了动态 Upstream、黑名单、waf 等功能。 虽然自研成功,但在功能上却遗留了一些问题,比如:

  • 动态化只做到到 Upstream 维度
  • 需要 Reload 才能带出新域名
  • 底层设计简单,功能扩展能力不强

后续我们对 API Gateway 功能有了强需求后,开始去调研相关的开源网关产品。

为什么选择了 Apache APISIX#

实际上 2019 年年底开始调研网关产品时,Kong 算是一个比较流行的选择。

但后续经过测试发现,Kong 的性能不太能满足我们的需求,同时我们认为 Kong 的架构不是很优秀:因为其配置中心选用 PostgreSQL,所以 Kong 只能利用非事件驱动去更新路由,依赖每个节点去刷新路由。

进一步调研时,我们发现了 Apache APISIX。首先 Apache APISIX 的性能比 Kong 强,在 Apache APISIX 的 GitHub Readme 中有个非常详细的对比图,列出了两者的性能测试差距,这与我们自己测试下来的数据基本一致。

Apache APISIX 与 Kong 性能对比图

在架构方面,Apache APISIX 的 etcd 配置对我们而言是一项更优的选择。

Apache APISIX 架构

当然,最主要的原因是我们觉得社区也很重要。社区如果活跃,在版本更新迭代、问题解决和功能优化上的速度就会很快。从 GitHub 和平时的邮件反馈中我们看到了 Apache APISIX 社区的活跃,为产品功能和稳定性提供了强有力的保证。

网关平滑迁移经验分享#

大部分朋友在开始接触 Apache APISIX 时,都会用 CLI 去生成配置并起实例。但在我们做平滑迁移的过程中,并没有使用 CLI 去生成配置。

主要原因是 Apache APISIX 在 OpenResty 中会生效一些 Phase,比如初始化 init、init_worker、HTTP 和 Upstream 相关 Phase 等。对应到 Apache APISIX 的配置后我们发现,这些都可以脱离 CLI 而存在。

所以基于上述原因,我们最终采取了如下行动进行平滑迁移:

  • 不使用 Apache APISIX 的 CLI 生成配置
  • 引入 Apache APISIX 的 Package Path 并将 Apache APISIX 作为 Default Server
  • 保留其它静态配置中的域名,由于新域名未在静态配置中,将 Fallback 到 Apache APISIX
  • 最终将静态配置逐渐迁移到 Apache APISIX 中

当然,除了上述方法,我们也给大家推荐一种「轻混模式」,即使用静态配置配合 Apache APISIX 作为 Location,引入前边提到的一些 Phase 或 Lua 代码进行配置即可。这样做可以在静态配置中引入一些特殊配置,实现动态化等效果。

基于 Apache APISIX 的 Shared State 改进#

首先在我个人看来,「转发效率一定不是问题,而 Shared State 是影响稳定性的最大因素」,为什么这么说?

因为转发效率可以通过横向扩容去解决,但 Shared State 是所有的节点共享的,所以是至关重要的模块。

所以在使用 Apache APISIX 后,我们主要针对 Shared State 层面进行了一些调整与优化。

优化一:多台机器监听下的 etcd 架构优化#

一般公司网关架构中,都会涉及多台机器,有的可能多至几百台,同时每台机器还要顾及 worker 数量。所以当多台机器监控相同 Key 时,etcd 的压力就会比较大,因为 etcd 的其中一个机制是为了保证数据一致性,需要所有事件返回给监听请求后才能处理新请求,当发送缓冲满了后就会丢弃请求。所以当多台机器同时监听时就会导致 etcd 超时运行,提示 Overload 报错等状况。

针对上述问题,我们使用了自研的 etcd Proxy。之前 Apache APISIX 与 etcd 的连接关系如下图左侧所示,每个节点均与 etcd 连接。所以作为一个大规模入口时,连接数量会特别大,对 etcd 造成压力。

etcd Proxy

既然是监听相同的 Key,我们做了一个代理来进行统一监听,当有结果反馈时,再返回给 Apache APISIX。具体架构如上图右侧所示,在 Apache APISIX 和 etcd 中间放置了 etcd Proxy 组件来监控 Key 值的变化。

优化二:解决路由生效过程中的性能问题#

随着公司规模提升,路由数量的增长也会随之而来。我们在实践过程中发现在每次路由更新时,Apache APISIX 都会重建用来匹配路由的前缀树。这个主要是由于 table.sort 性能不足所导致的。

在实践过程中,我们观察到路由频繁更新时,网关 CPU 升高、丢包率升高,进一步排查后发现丢包率升高的主要原因为 Listen overflow 所造成。

CPU 火焰图

在 CPU 升高现象上,通过火焰图可以明显看到大部分 CPU 的时间都是划在 auxsort 上,它是由 FUNCC 触发。而 FUNCC 的触发也指明了一个问题,就是证明相关数据没有经过 LuaJIT,只有图中最右侧的一小部分处理了正常请求。

出现这种现象的原因主要是 LuaJIT 的 table.sort 不是完全依靠 JIT 模式,这点可以在 LuaJIT 官网 wiki 中看到相关说明,所以在 Lua 代码环境中使用 table.sort 效率是比较低的。

LuaJIT Wiki

针对这个问题,我们自己使用纯 Lua 代码实现了针对上述场景的 sort 配置进行了解决,但其实 Apache APISIX 在之后的版本更新中已经修复了这项问题,具体思路也跟我们理解的类似。

更多 Shared State 使用经验#

  1. 在修改 Apache APISIX 或者自己进行插件开发时,确保做好 Schema 校验,包含判空,尤其是在匹配部分。因为在匹配部分出问题的话,会造成整体性的影响。
  2. 做好业务拆分规划。根据业务量去规划好相关 etcd Prefix 和 IP 数量,部署更稳固的集群,把系统性风险降到最低

开源话题讨论#

稳定性与功能层面的取舍#

目前金山办公使用 Apache APISIX 已经快两年了,作为产品用户,我认为 Apache APISIX 确实是一款稳定可信的开源产品,在绝大多数情况下,都会及时地与社区最新版本保持一致。

但是一般接触并应用过开源产品的公司应该都有体会,升级版本会有一些新功能的出现,但同时也会带来一些稳定性上的问题,所以在升级版本和稳定性中我们应该如何取舍。

这个问题肯定没有统一的答案,但是我个人觉得针对 Apache APISIX 这项产品,尽量与官网版本保持一致。

就金山办公而言,我们目前因为大规模使用到 Apache APISIX,所以对稳定性有极致追求。之前跟不上官方更新进度时也对我们的使用造成了一定程度的影响,所以推荐大家尽量与官方版本保持一致。

如果说你像我们一样,有时候可能跟不上官方版本,至少也应该做到每周查阅 GitHub 的 Master Change Log 等相关文档,时刻关注产品变化。

基于 Apache APISIX 产品化经验#

我们基于 Apache APISIX 包装了很多产品化功能,比如多机房应用比例切量、一键封禁路由等。在实践应用过程中,我们认识到 Apache APISIX 是一个极其灵活强大的产品,所以在进行产品化改造时我们就应该明白一个点:强大 = 避免不了的复杂和危险。

这点在 Apache APISIX 本身的代码设计上也有很多的体现,比如一些插件的改造可能就需要自己去编译,因为毕竟各自应用起来时场景没有办法做到统一。

最后,基于我们前边提到的实践经验,也建议大家在进行 Apache APISIX 项目产品化时,提前规划好网关共享的颗粒度,减少后续使用问题。