为什么要API网关
笔者所在的是一家电商公司,流量的不确定性是我们作为架构人员最头疼的问题。日常的时候,整站可能只有很少的流量,你可以安心的点一杯咖啡,享受安静的撸代码的时光。但是,一旦遇到猝不及防的活动,比如商家搞秒杀,或者被数据被爬,流量远超预期的时候,很多情况下,都会让开发人员欲哭无泪。所以,我们会做很多的优化来提高我们的系统负载能力:
缓存
缓存是最常用,且必要的手段。因为数据库的处理能力和连接数都是很宝贵的资源,很多有经验的程序员都会在数据库层之上添加多级缓存以达到提供系统吞吐量的目的。比如,在数据库前添加一个集中式缓存(如redis),在redis层之上再添加一个内存cache(如Guava)。现在也有很多框架实现了类似的多级缓存,如jetCache。当然,引入缓存带来的数据一致性问题是使用过程中需要处理的一个难点,这里就不展开,有很多的框架都有自己实现的一套处理数据一致性问题的方案,可以多关注一些别人的实现。
但是,缓存的引入能够解决所有的问题吗?显然是不能的。哪怕集中式缓存的能力也是有限的(只不过比数据库这类的上限高很多);内存缓存的处理使用CPU和RPC连接的使用在机器不变的情况下也是有处理上限的,扩容也许可以解决问题,但是成本是否是可以接受的呢?
限流
限流是很多时候我们保证系统可用性,提供用户有损服务的终极武器。我们会在我们的应用中加入接口限流逻辑来保证自己的服务不会因为某个接口的调用导致整体服务的不可用。现在使用比较广泛的,比如阿里开源的sentinel。但是在应用层再进行限流是不是太过于靠后了,我们是不是可以再提前到更前面就拦截掉?这样会不会体验更好,反应更快?
熔断/降级
熔断可以通过hystrix很容易的加入你的代码中,但是熔断的重点不是代码,而是,何时进行熔断?熔断的策略怎么设置?会不会导致误杀?这才是熔断的难点。降级可以是在熔断策略生效的情况下进行的升级,或者也可以通过手动来进行降级,比如阿里双十一大促的时候对非关键服务或者接口的降级,空出资源来给大促相关的服务使用。
异步化
同步转异步是为了削峰,比如说我们把请求通过MQ转成异步的处理方式,返回给用户一个待处理的结果。这种方式要看业务是否允许进行异步处理。其实这种处理方式和缓存的问题是一样的,哪怕转了异步,异步队列或者MQ的容量和处理能力是有限的,不能解决根本的问题。
API网关需要处理的问题
限流
根据以上的问题,我们最初考虑的时候只是单纯的想把应用层的限流提前到NG。在日常机器固定的情况下,针对核心接口,我们通过线下和线上的压测,都给出了日常能够提供正常服务的限流值,超过限流配置的请求数,我们会返回默认的固定返回码和文案(当然,也可以配置特定的返回码和文案):
{
"retCode": "999999",
"retMsg": "亲,太火爆了,请稍后再试!"
}
如果能够和前端协同,我们还可以通过返回码实现接口降级,既在单位时间内不允许任何请求通过,直接返回我们配置好的文案即可实现降级。
缓存
NG是提供接口的返回的cache的,但是配置完全依赖于运维在NG上的操作,维护不方便,灵活性差。我们通过配置的方式,可以为NG的缓存提供动态的配置方式。
对于某些情况下可以一段时间内降级或者返回固定返回值的接口,我们还提供配置固定返回结果的配置方式,极大的优化了NG缓存的使用体验。
安全
作为一个面向C端用户的平台,它的开放性也决定了会有很多各式各样的人来使用我们的平台,当然也包括黑产,如果作为有一定知名度的平台,还可能会有一部分友商来调研或者获取平台的一些信息,如何对非正常的用户进行限制?
一开始可以简单的对比如简单用户访问行为进行限制,比如对于操作过于频繁的用户进行访问频率限制,单位时间内一个用户只允许访问一定的次数;对于核心接口,还可以通过通过重定向的方式,和前端结合起来,为其弹出一定的验证策略(比如图块验证等);而对于部分有实力的公司,一般都会有数据安全部门,会对用户的行为进行刻画得到用户画像信息。数据安全团队通过用户画像判断有风险的客户,把他们推送到网关的存储中,网关也可以通过刚才的方式进行限制访问或者安全策略校验。
预发布和灰度发布
没一个软件开发的公司都会存在多套环境用于不同的用途,开发版本会在这些环境中逐步递进,最终到生产环境。但是无论再怎么小心翼翼,进阶的过程因为环境,配置的不一致,总会是不是出现这样或者那样的问题,怎样才能避免或者把影响进行缩小?部分成熟的互联网企业都会通过预发布,金丝雀或蓝绿发布来保证发版过程是可验证,易回滚,范围渐进式的一套规范。
通过k8s部署的服务,我们给出了2套不同namespace的集群,都注册到我们nginx中,网关可以配置白名单用户,或者根据流量的百分比进行流量引流到灰度集群中。应用发版时,可以先在灰度集群中,将测试,产品,运营等人加为白名单进行功能验收,此时可以把它作为一个预发布环境;验收完成,开发人员也可以通过流量百分比进行引流,观察后端的服务是否有异常,有异常及时切断流量,排查修改,没有问题则可以开始进阶到生产,此时,可以把它作为一个灰度环境。
当然,把这种集群部署和流量路由方式作为一个灰度发布环境有不合理的地方,但是把预发布环境合理的利用起来,引入真实的C端用户,不失为一个合理利用资源的方式。
更近一步,我们未来预期会加入前端灰度的支持。考虑这样一种场景:前端需要发布一个功能,这个功能页面只准备给某个特定的人群看到,甚至于给不同的用户展示不同的页面细节,这时候我们就需要加入前端灰度的功能。我们可以在网关中加入加入需要灰度的功能,并配置用户群信息。前端启动APP或者定时来询问用户是否被前端灰度功能命中,我们即可实现前端灰度的功能。
总结
以上只是我个人的一些浅显理解,其实现在也有一些厂商开源了他们的API网关,比如社区比较活跃的incubator-apisix(点击访问);出现比较早,使用较多的kong(点击访问),它们的实现方式大同小异,都是通过lua+openResty来实现(我们也是一样),如果你们没有足够熟悉或者愿意去尝试的lua+openresty开发人员,你可以通过已有的这些开源组件去进行插件的扩展,它们都有提供成熟的插件开发默认提供参考。后续有可能,我会做几篇这些开源组件的分析和我们自己实现的源码分析文章,敬请期待!
如果有什么想交流的,欢迎留言,共同探讨!