API网关的开源解决方案那么多,为什么我们却还要选择自研?

Posted 聊聊架构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了API网关的开源解决方案那么多,为什么我们却还要选择自研?相关的知识,希望对你有一定的参考价值。

作者|杨果
编辑|郭蕾

API Gateway/Backend for Front-End 作为一种目前非常流行并且经过验证的 Pattern,不论是在 Netflix/Amazon 还是 BAT 都得到了广泛的应用。在 Microservice architecture pattern 大行其道的当下,API Gateway 的建设显得尤为重要,本文主要是分享笔者在 API Gateway 研发中的一些心得体会。

另外,作者在2017年3月时曾有分享过他是如何帮助红星美凯龙搭建研发框架的实践,感兴趣的同学可以。

Context

为了阐述API Gateway建设带来的收益是值得我们投入大成本来研发的,就以笔者所在的小笨鸟跨境电商平台的业务来说明。

小笨鸟不但要能够将国内卖家的产品一键发布到Amazon、eBay、Newegg等三方平台,同时我们还需要为客户建设独立的Online Store,从而更利于品牌的推广。就以Online Store为例,为了支撑整个电子商务逻辑的运转,可以简单抽象成如下的微服务:

  • shopping cart

  • shipping

  • inventory

  • recommendation

  • order

  • review

  • product catalog

架构如下图所示:

API网关的开源解决方案那么多,为什么我们却还要选择自研?

Gateway 的职责如下:

  • 安全

  • 流控

  • 缓存

  • 鉴权

  • 监控

  • 日志

  • 协议转换

  • …..

可以想象在 Microservice 的架构体系之下,如果没有统一的地方去处理这些跟业务关联度不是很紧密但是又必不可少的逻辑,那么每个上面需求将会在每个 Microservice 都实现一遍,这种代价非常的高昂。当然任何事物都存在双面性,有好就必然有坏的一面,流量全部都要经过 Gateway,所以它一定要非常稳定且高可用,假如一旦此处出现问题,那么可以想象问题的严重性。

技术选型

技术选型从来就不是一件容易的事情,虽然笔者没有所谓的选择恐惧症,但是想到要拦截所有的 7 层流量,心里面还是难免有点犯怵。业界开源的方案有 Netflix/zuul,Kong/kong,openresty/openresty + 自研,TykTechnologies/tyk,Netty + 自研。

这些都是非常不错的方案,并且已经经过了大量的生产实践,那么剩下的事情就只有根据自身的实际情况来选择。经过仔细分析,我们希望 API Gateway 足够薄,不希望在其上附加太多的功能。如果单纯的从性能的角度来考虑,Kong / Openresty 估计是最好的,毕竟底层都是 nginx。虽然在 Openresty 平台上做过一些小的模块,但是毕竟在这个上面算不上专家。

Zuul 是 Java 平台上非常好的选择之一,有 Netflix 和 Pivotal 的支持并且功能强大,但是考虑到跟 Netflix 的全家桶集成很紧密,组件很多且复杂度较高,我们的系统主要还是架构的 Dubbo 之上,Spring Cloud 用的地方不多。该项目的发起经历了两个阶段,初期我们是想做 WAF 把我们系统的安全给提升上来,并没有一上来就想做 API Gateway,虽然这两者从整体架构上来说是没有本质上的差异。

随着 WAF 的研发完成,我们觉得这一层可以做更多的事情,这才有了 API Gateway 的构想。随后我们在 WAF 的基础之上逐步新增了很多功能,就如后面我们会讨论的架构图所示,WAF 的拦截最终变成了我们 Filter 中的一环。当时由于笔者有多年使用 Netty 的经验,所以我们在设计 WAF 是就选用 Netty。

考虑到 Netty 还是太偏底层,所以选择了构建在 Netty 之上的 LittleProxy 作为我们的 HTTP Proxy,这样就避免了我们完全重头开发。LitteProxy 的 Programmers 也是 LANTERN 的维护者。关于蓝灯的介绍,可以移步维基百科。LittleProxy 只有二十来个类十来个接口,从头到尾通读一遍代码也就 2/3 天的时间,并且从 GitHub Star 数和 Stack Overflow 的收录数量来看,它都是不错的选择。作为 API Gateway 中最重要的部分确定之后,其实剩下的困难点就不多了。

如果当时我们一开始就想好了要构建一套完整的 API Gateway,是不是现在就是例外的选择了,也许是也许不是,但是这些都不重要,重要的是选择了就得尽最大的努力让你别为自己的选择而后悔!

架构设计

在讨论我们的架构设计之前,先让我们来欣赏下 zuul 的架构,如下图所示:

API网关的开源解决方案那么多,为什么我们却还要选择自研?

上图中 Zuul Servlet 是 HTTP 流量的接入点,Zuul 当然也不例外的使用 Netty 作为它们网络通讯的核心。ZuulFilter Runner 是 Gateway 的业务核心,Filter type 分为三种类型,Pre routing/Routing/Post routing,其实就是我们所谓的 AOP。虽然 ZuulFilter 被分为了三种类型,但是其实它们是共享一个 Request Content,这一点非常重要,ZullFilter 的执行流程如下图:

API网关的开源解决方案那么多,为什么我们却还要选择自研?

一个 HTTP Request 进来首先要通过 pre filter 的检查,如果没有任何问题,就交给 routing filter 让其把流量转发给 origin server,然后 origin server 会返回响应结果给下游的 post filter。以上两步任何一步出现问题就直接到 error filter,error filter 也会将流量转给 post filter。最终都是由 post filter 来对 response 进行整形,然后返回给上游的代理,最终到达用户。Zuul 为了方便用户定制并且动态化加载配置引入了 Groovy,这是非常好的地方,也就是说我们享受到静态化的性能,同时又能享受到动态化的灵活。右侧和底部的设计是用来管理 Zuul 相关配置的,包含服务的注册发现、Filter 的管理、统计分析相关的很多东西。

分析完 Zuul 之后,我们丛中吸收了很多东西,下图是我们的架构:

从上图可以看出,我们精简了 Zuul 从而更关注其核心的部分,因为我们的目的不是构建一套功能强大的中间件,而是一套非常精简的 API Gateway。

研发过程

该项目非常精简,真正核心的类估计也就 5 个左右,非常适合阅读和练习。关于 WAF 的技术过程中遇到的问题,我也写了两篇文章来说明,详见 Java 版 WAF 实现细节和 HttpProxy 研发心得(文末有链接)。WAF 中的有些特性其实在 API Gateway 中用到的概率较小,比如 ShadowSocks/Socks5 的支持,如果这些大家不需要可以移除,这样代码就更少更精简。

如果想让 WAF 要真正的成为 API Gateway,还有很多的事情要做,例如 Zuul 中 groovy 自定义 Filter 以及管理功能,这些都需要根据需求定制。我们内部的版本也正在逐步完善,一些我觉得有用的特性也会同步到 WAF 中去。目前我们的 API Gateway 还非常简单,例如路由我们目前只支持 Host,不支持 URL 的 regex,而且很多管理功能都还处于开发阶段,但是从目前运行的情况来看还是非常稳定。

对于自研 Gateway,其实难度还是不小,并不像看上去的那么美。如果对网络编程不是很熟悉,特别对 TCP/HTTP 理解不是比较全面,建议要慎重,因为这样风险太大。我们在研发过程就碰见很多的问题,下面就简单的列举几个:

  • HTTP Header 头的大小写问题

  • CORS OPTIONS 不受 XHR 的控制

  • Netty 堆外内存泄漏

  • Origin Server 心跳检测

  • Request Content-Type 数据传递的区别

  • 根据 URL Regex 路由

  • ……

网络上关于 kong/zuul 的文章非常多,关于自研的却不是很多,所以分享出来供大家参考,希望大家在做方案的时候有更多的选择和思考。

文章中提到的两个URL如下:

- https://www.yangguo.info/2017/11/13/HttpProxy研发心得/

- https://www.yangguo.info/2017/06/06/Java版WAF实现/


以上是关于API网关的开源解决方案那么多,为什么我们却还要选择自研?的主要内容,如果未能解决你的问题,请参考以下文章

Kafka的API那么多,到底该怎么选?

(原创)大话微服务架构之api网关-apiGateway-技术选型

开源 API 网关架构分析

开源微服务API网关,单核2万QPS,今年最值得学习的开源项目

为什么需要API网关?

开源微服务API网关,单核2万QPS,今年最值得学习的开源项目