vlambda博客
学习文章列表

API网关在北科的实践与应用

一、什么是API网关?

1、缘起

       一个架构的出现从来都不是空穴来风,API网关作为服务端与客户端之间的重要桥梁,自然必有其独到之处,想要了解API网关,那我们还是先从头说起。

       很久以前,我们还只有一个服务,这个服务有一个专享的域名,有一套自己的完整路由体系,为了防止恶意用户攻击,实现了鉴权、限流、熔断、IP黑白名单、监控等一系列的手段来保护自己,这一切都运行良好。

2、问题

       直到某一天,我们的服务越做越大,所有的代码都在一个项目里耦合严重不利于维护,我们需要把一个服务拆分成了多个小的服务,于是乎微服务诞生了。

       细心的同学可能会发现一些问题,那些与实际业务逻辑无关的代码(鉴权、限流等),是每一个微服务都需要的基础功能,各自实现一遍似乎在重复造轮子,并且多个微服务诞生了多个域名,我们不可能给每个微服务单独申请一个外网域名,这样增加了客户端的复杂度,毕竟客户端不需要关心服务端有多少个微服务,客户端只需要知道路由其实足够了。

3、解决

       讲到这里,终于轮到我们的主角API网关出场了。首先我们需要统一流量的入口,所有的微服务流量需要先经过网关,通过网关再反向路由到各个微服务中。其次,各服务的通用功能收敛到网关层实现,避免各自重复造轮子。此时在客户端眼里,各个微服务会被当作一个整体的服务,屏蔽了服务内部细节,统一对外接口输出。

4、网关能力小结

使用API网关有以下优势:

  • 反向代理 / 负载均衡 / 健康检查

  • 统一入口 / 安全防护 / 流量识别 / 流量监控

  • 身份认证

  • 限流熔断

  • 灰度分流

  • 其他通用能力(例如Passport登录校验等)

二、API网关对比
筛选主流网关做对比,详细如下:
名称 Kong
Zuul
Spring Cloud Gateway Tyk
语言
Lua Java Java Go
高并发
基于Openresty 基于Netty的异步IO 基Netty的异步IO 基于Go
社区star
30k
11.2k
3.1k
6.6k
扩展性

官方插件

自研插件
需要开发 过滤器 插件
存储

Postgres

Cassandra

Yaml
内存文件

编写代码

Yaml

配置中心
Redis
健康检查
支持
支持
支持 支持
鉴权

Key、OAuth2.0

JWT、HMAC

Passport(自研)
需要开发

普通鉴权

Oauth2.0

Oauth2.0

JWT

HMAC
限流
支持 需要开发 通过ip、用户、路由限流,通过接口开发扩展 支持
监控

Datadog

Prometheus

zipkin
需要开发 gateway metrics filter 工具套件
生命周期
开发插件 需要开发 需要注册服务到eureka 开发插件
安全

机器人探测

防攻击
需要开发 需要开发 插件支持
可维护性

社区活跃

可开发插件

支持UI面板
可开发过滤器

Spring Cloud系列

Gateway资源较少

资料较少

支持UI面板

注:数据部分来源于网络

小结:

       公司团队对Nginx有比较丰富的维护经验,更偏向基于Openresty相关生态的开源解决方案。Zuul与Spring Cloud Gateway由Java编写,属于Java生态系统的一部分。对于Tyk而言,社区资料相对较少,虽然与Kong相比功能较为齐全,但Kong站在了巨人Nginx的肩膀上,天生拥有高并发特性,从Star数远超其他网关来看,Kong的受众用户更广,生态也更齐全。另外,在网关的选择之初,我们也关注过APISIX,但当时APISIX开源时间较短,组件不够丰富,因此没有纳入考虑。

       当然Kong也有缺点,Kong的集群支持需要Postgres或者Cassandra数据库,引入这两种数据库会增加我们的运维成本,因此我们对Kong做了二次开发,引入了MySQL集群模式。其次,Kong的管理界面只有企业版才能使用,这部分需要额外开发支持。最后一点是Kong采用Lua语言开发,想要阅读源码或者开发新的插件都必须学习Lua语言,幸运的是,Lua语言的学习成本较低。

三、Kong是如何运行的

       Kong的网关是架构在了Openresty上,Openresty共有11个执行阶段,详见下图:

       Kong将自己的代码嵌入进了这11个执行阶段中,从这份Nginx配置文件中,可以看到Kong如何实现了自己的执行计划:    

API网关在北科的实践与应用

       因此想要了解Kong的话,我们只需要按照这份配置文件的顺序,去梳理Kong的源码逻辑即可。比如,在kong.init阶段,完成了配置文件的加载,路由的初始化等,在kong.init_work阶段,完成了全局事件的加载。而我们的插件,基本都执行在了kong.rewrite与kong.access阶段,在这两个阶段中,我们可以对请求进行鉴权,对非法请求进行拦截,可以进行限流处理,甚至可以灵活修改请求头信息,以实现我们的特定逻辑。在kong.header_filter与kong.body_filter阶段,完成对请求头与请求内容的最后过滤。而在最后的kong.log中,可以用来实现监控功能,由于此时已经响应了业务的请求,因此监控并不占用业务请求时间。

API网关在北科的实践与应用

       上述的源码是Kong在rewrite的阶段执行的,主要工作就是对已加载的插件进行遍历,然后执行各个插件的rewrite方法,其他阶段的插件执行基本也都是这个逻辑。因此简单来说,Kong插件加载就是在Openresty的各个执行阶段,对符合规则的插件进行遍历,然后执行这个节点插件对应的方法。

四、Kong的特性

1、集群扩展性

       得益于Kong的集群模式,我们可以简单的添加更多的机器,来支持更高的并发访问。目前共有四种集群模式,分别是Yaml配置文件、Postgres、Cassandra、MySQL(官方不支持MySQL,此处为二次开发)。

       Kong的事件轮询机制,会每间隔1s扫描数据库中的表,来动态的更新自己缓存中的路由信息。当某个服务收到了变更路由请求,会在数据库中的cluster_event表中添加一条变更信息,其余服务轮询到该变更信息时,触发自己的变更动作,最终集群里的所有服务路由达到一致状态。

2、插件化

       插件化是Kong的另一个重要特性,因为除了基本的路由功能外,几乎所有的其他类似于鉴权、限流、监控等功能,全部都使用了插件来实现。

       Kong的开源插件不能完全满足我们的业务需求,为了适配同城的众多业务场景,我们针对HMAC鉴权插件、Rate-Limiting限流插件、IP黑白名单插件、Request-Transformer、Prometheus等插件都做了二次开发,目前线上运行稳定。

       此外,为了简化业务同学的研发成本,我们研发了Passport插件与Auth插件。业务代码不需要在每个接口中重复的调用checkLogin接口校验用户是否已登录,这一切都封装在了网关的插件中。

3、多平台

       Kong支持在多种基础环境中运行,可以安全运行在我们线上广泛使用的Centos系统中,并且如果我们后期拥抱Docker甚至于Kubernetes,Kong都可以运行良好。

五、Kong的性能

       前面提到过,Kong站在了巨人Nginx的肩膀上,继承了其异步非阻塞的能力,因此天生拥有高并发的特性。为了避免读库时拖了Nginx的后腿,Kong内部也实现了两种缓存机制,分别是work内的LRU缓存与work间的share_dict共享内存。但具体实际的性能表现如何,还是看一下具体的压测结果吧。

Avg(ms) TP90(ms) TP99(ms) RPS
原服务直接访问 1.28ms 1.49ms 2.60ms 78554.00
Nginx Proxy 1.49ms 1.72ms 2.95ms 67618.42
Kong+Yaml 1.69ms 2.17ms 4.05ms 59823.12
Kong+MySQL 1.77ms 2.37ms 4.30ms 56816.23

注1:原服务是Go语言实现的一个mock服务,收到请求后会返回一个大小1k的response body;

注2:网关服务机器配置为8c/16g;原服务机器配置为4c/8g;

注3:压测过程中,关闭了所有的网关插件,仅测试转发性能;

注4:wrk压测参数为 wrk -t4 -c 100 –latency;

从压测结果来看,转发性能完全可以满足业务的需要。

六、Kong在北科架构中的应用场景

       从下图可以看出,我们目前有两层负载均衡,分别是腾讯云的四层负载均衡与SLB的七层负载均衡。我们在SLB之下,内网服务之上,引入了网关层,以此来为内网服务提供更便捷更安全的保障。

API网关在北科的实践与应用

       下图中把网关层单独拿出来放大,可以看出对于网关而言,依托于插件机制是非常灵活的。我们可以根据配置文件或者数据库,给网关增加或者减少某种插件,使得网关可以快速的增加或者减少某种能力。

API网关在北科的实践与应用

       值得一提的是,我们在PIE3.0(PHP集成开发环境)中也引入了对Kong的支持,也就意味着所有升级了PIE3.0的用户,天生拥有以上能力,并且日后对Kong所做的所有插件支持,都可以无缝与PIE3.0对接。

七、Kong的监控

       为了监控网关集群与下游业务的运行状态,我们搭建了基于Prometheus存储的Grafana监控面板,主要提供了关于Nginx共享内存监控、Nginx连接相关监控、RPS监控、带宽监控、访问延迟监控等多个维度的监控信息,详见下图:

API网关在北科的实践与应用

八、在Kong上碰到的问题及解决

1、Kong的集群模式不支持MySQL

       由于我们北科尚未接入Postgres、Cassandra两种数据库,引入两种数据库无疑会大大增加我们的运维成本。因此,我们针对Kong做了二次开发,新增了对MySQL的适配。目前MySQL版本的Kong集群,在Op科技防火墙模块中运行良好。

2、没有合适的管理平台

       Kong企业版有出付费版的管理平台,但是收费较贵。

       另外调研了两款开源的管理平台 Kong-Dashboard 与 Konga。其中,Kong-Dashboard对我们当前的版本兼容性较差,直接排除。Konga的兼容较好并且界面风格使用起来也更顺畅,但是Konga的权限粒度较粗,并且Konga里有一些配置是我们永远可能都用不到的,会增加研发同学的使用复杂度。

       综上所述,我们计划根据自己的管理平台,简化原生配置,增加更细粒度的比如某个按钮级别的权限粒度,保证网关的稳定性。

3、网关在某业务线上线后,线上经常有如下报错:upstream prematurely closed connection while reading response headerfrom upstream

       阅读Nginx源码后发现,此报错发生在Nginx的upstream阶段,当对fd描述符使用recv命令时,发现fd对应的对端连接已经关闭,因此Nginx此时不得不放弃当前连接。

       检查Java服务的idleTimeout(空闲连接超时)设置为30s,而这个参数在Nginx里配置为60s。当Java发现连接30s超时后,发起四次挥手关闭连接。关闭连接的过程中如果Nginx同时有请求过来,就会造成这个报错。

       修复过程:调整Nginx的参数由60s改为25s,使得Nginx方先关闭连接。修改参数后,后续没有相关的报错了。

4、网关在某业务线上线后,使用IP-Restriction黑名名单插件,无法拿到用户的真实IP

       此处对IP-Restriction插件做了二次开发,使其可以从HTTP中先后检查头信息中X-Real-Ip、x_forwarded_for、remote_addr对应的值,此时即可以拿到用户的真实IP。

5、Prometheus插件的性能较差

       业务线同学在压测自己服务时,发现Prometheus 0.6版本插件性能较差,因此我们做了一版详细的对比,表格如下:

Avg(ms) TP90(ms) TP99(ms) RPS
原服务直接访问 1.29ms 1.51ms 2.82ms 78307.02
Nginx Proxy 1.53ms 1.85ms 3.21ms 65265.46
Kong不含插件
1.64ms 2.03ms 3.60ms 60877.41
Kong+Prometheus 0.6 6.68ms 8.68ms 13.00ms 14979.34
Kong+Prometheus 0.9 1.76ms 2.25ms 4.39ms 57705.56

注1:原服务是Go语言实现的一个mock服务,收到请求后会返回一个大小1k的response body;

注2:网关服务机器配置为8c/16g;原服务机器配置为4c/8g;

注3:压测过程中,关闭了所有的网关插件,仅测试转发性能;

注4:wrk压测参数为 wrk -t4 -c 100 –latency;

       从压测结果来看,0.9版本比0.6版本性能提升了不少,并且CPU消耗也更少,且与Kong不含插件时,性能差异很小。升级该插件至0.9解决了该性能较差的问题。

九、写在最后

       API网关作为一款管控流量入口的中间件,已经在公司的多个业务线落地,在一定程度上满足了绝大多数的现有需求。Kong有着不错的扩展能力,使得我们可以通过自研插件作为补充,解决更多的问题,Kong有着活跃的社区,我们从社区中学到了很多的经验,有一些开源插件完美的适配了我们的业务场景,后续我们也会逐步回馈开源社区,相互促进。

END