For investors
股价:
5.36 美元 %For investors
股价:
5.36 美元 %认真做教育 专心促就业
我们在访问互联网的时候,除了需要有自己的IP端口以外,还需要将我们的访问数据通过网关来传输到互联网之中。今天,我们就一起来了解一下,API网关的设计都有哪些。
网关一词较早出现在网络设备里面,比如两个相互独立的局域网段之间通过路由器或者桥接设备进行通信,这中间的路由或者桥接设备我们称之为网关。
相应的API网关将各系统对外暴露的服务聚合起来,所有要调用这些服务的系统都需要通过API网关进行访问,基于这种方式网关可以对API进行统一管控,例如:认证、鉴权、流量控制、协议转换、监控等等。
API网关的流行得益于近几年微服务架构的兴起,原本一个庞大的业务系统被拆分成许多粒度更小的系统进行独立部署和维护,这种模式势必会带来更多的跨系统交互,企业API的规模也会成倍增加,API网关(或者微服务网关)就逐渐成为了微服务架构的标配组件。
如下是我们整理的API网关的几种典型应用场景:
1、面向Web或者移动App
这类场景,在物理形态上类似前后端分离,前端应用通过API调用后端服务,需要网关具有认证、鉴权、缓存、服务编排、监控告警等功能。
2、面向合作伙伴开放API
这类场景,主要为了满足业务形态对外开放,与企业外部合作伙伴建立生态圈,此时的API网关注重安全认证、权限分级、流量管控、缓存等功能的建设。
3、企业内部系统互联互通
对于中大型的企业内部往往有几十、甚至上百个系统,尤其是微服务架构的兴起系统数量更是急剧增加。系统之间相互依赖,逐渐形成网状调用关系不便于管理和维护,需要API网关进行统一的认证、鉴权、流量管控、超时熔断、监控告警管理,从而提高系统的稳定性、降低重复建设、运维管理等成本。
设计目标
纯Java实现;
支持插件化,方便开发人员自定义组件;
支持横向扩展,高性能;
避免单点故障,稳定性要高,不能因为某个API故障导致整个网关停止服务;
管理控制台配置更新可自动生效,不需要重启网关;
应用架构设计
整个平台拆分成3个子系统,Gateway-Core(核心子系统)、Gateway-Admin(管理中心)、Gateway-Monitor(监控中心)。
Gateway-Core负责接收客户端请求,调度、加载和执行组件,将请求路由到上游服务端,处理上游服务端返回的结果等;
Gateway-Admin提供统一的管理界面,用户可在此进行API、组件、系统基础信息的设置和维护;
Gateway-Monitor负责收集监控日志、生成各种运维管理报表、自动告警等;
系统架构设计
说明:
网关核心子系统通过HAProxy或者Nginx进行负载均衡,为避免正好路由的LB节点服务不可用,可以考虑在此基础上增加Keepalived来实现LB的失效备援,当LBNode1停止服务,Keepalived会将虚拟IP自动飘移到LBNode2,从而避免因为负载均衡器导致单点故障。DNS可以直接指向Keepalived的虚拟IP。
网关除了对性能要求很高外,对稳定性也有很高的要求,引入Zookeeper及时将Admin对API的配置更改同步刷新到各网关节点。
管理中心和监控中心可以采用类似网关子系统的高可用策略,如果嫌麻烦管理中心可以省去Keepalived,相对来说管理中心没有这么高的可用性要求。
理论上监控中心需要承载很大的数据量,比如有1000个API,平均每个API一天调用10万次,对于很多互联网公司单个API的量远远大于10万,如果将每次调用的信息都存储起来太浪费,也没有太大的必要。可以考虑将API每分钟的调用情况汇总后进行存储,比如1分钟的平均响应时间、调用次数、流量、正确率等等。
数据库选型可以灵活考虑,原则上网关在运行时要尽可能减少对DB的依赖,否则IO延时会严重影响网关性能。可以考虑次访问后将API配置信息缓存,Admin对API配置更改后通过Zookeeper通知网关刷新,这样一来DB的访问量可以忽略不计,团队可根据自身偏好灵活选型。
非阻塞式HTTP服务
管理和监控中心可以根据团队的情况采用自己熟悉的Servlet容器部署,网关核心子系统对性能的要求非常高,考虑采用NIO的网络模型,实现纯HTTP服务即可,不需要实现Servlet容器,推荐Netty框架(设计优雅,大名鼎鼎的SpringWebflux默认都是使用的Netty,更多的优势就不在此详述了),内部测试在相同的机器上分别通过Tomcat和Netty生成UUID,Netty的性能大约有20%的提升,如果后端服务响应耗时较高的话吞吐量还有更大的提升。(补充:Netty4.x的版本即可,不要采用5以上的版本,有严重的缺陷没有解决)
API故障隔离及超时、熔断处理
在详细阐述设计前先讲个实际的案例,大概12年的时候某公司自研了一款ESB的中间件(企业服务总线跟API网关很类似,当年SOA理念大行其道的时候都推崇的是ESB,侧重服务的编排和异构系统的整合。),刚开始用的还行,但随着接入系统的增多,突然某天运维发现大量API出现缓慢甚至超时,初步检查发现ESB每个节点的线程几乎消耗殆尽,起初判断是资源不够,紧急扩容后还是很快线程占满,终导致上百个系统瘫痪。
终找到问题的症结是某个业务系统自身的原因导致服务不可用,下游业务系统请求大量堆积到ESB中,从而导致大量线程堵塞。
以上案例说明了一个在企业应用架构设计里面的经典原则-故障隔离,由于所有的API请求都要经过网关,必须隔离API之间的相互影响,尤其是个别API故障导致整个网关集群服务中断。
接下来分别介绍故障隔离、超时管控、熔断的实现思路。
1、故障隔离
有两种方式可以实现,一是为每个API创建一个线程池,每个线程分配10~20个线程,这也是常用的隔离策略,但这种方式有几个明显的缺点:
线程数会随着API接入数量递增,1000个API就需要2万个线程,光线程切换对CPU就是不小的开销,而其线程还需要占用一定的内存资源;
平均分配线程池大小导致个别访问量较大且响应时间相对较长的API吞吐量上不去;
Netty本身就有工作线程池了,再增加API的线程池,导致某些需要ThreadLocal特性的编程变得困难。
二是用信号量隔离,直接复用Netty的工作线程,上面线程池隔离提到的3个缺点都可以基本避免,建议设置单个API的信号量个数小于等于Netty工作线程池数量的1/3,这样既兼顾了单个API的性能又不至于单个API的问题导致整个网关堵塞。
2、超时管控
API的超时控制是必须要做的,否则上游服务即便是间歇性响应缓慢也会堵塞大量线程(虽然通过信号量隔离后不会导致整个网关线程堵塞)。
其次,每个API好可以单独配置超时时间,但不建议可以让用户随意设置,还是要有个大阈值。(API网关不适合需要长时间传输数据的场景,比如大文件上传或者下载、DB数据同步等)
实现上可以直接复用开源组件的功能,比如:HttpClient可以直接设置获取连接和Socket响应的超时时间,Hystrix可以对整个调用进行超时控制等。
3、熔断
熔断类似电路中的保险丝,当超过负荷或者电阻被击穿的时候自动断开对设备起到保护作用。在API网关中设置熔断的目的是快速响应请求,避免不必要的等待,比如某个API后端服务正常情况下1s以内响应,但现在因为各种原因出现堵塞大部分请求20s才能响应,虽然设置了10s的超时控制,但让请求线程等待10s超时不仅没有意义,反而会增加服务提供方的负担。
为此我们可以设置单位时间内超过多少比例的请求超时或者异常,则直接熔断链路,等待一段时间后再次尝试恢复链路。
实现层面可以直接复用Hystrix。
运行时配置更新机制
前面章节提到过出于性能考虑网关在运行时要尽可能减小对DB的访问,设计上可以将API、组件等关键内容进行缓存,这样一来性能是提升了,但也带来了新的问题,比如Admin对API或者组件进行配置调整后如何及时更新到集群的各个网关节点。
解决方案很多,比如引入消息中间件,当Admin调整配置后就往消息中心发布一条消息,各网关节点订阅消息,收到消息后刷新缓存数据。
我们在具体实现过程中采用的是Zookeeper集群数据同步机制,其实现原理跟消息中间件很类似,只不过网关在启动的时候就会向ZK节点进行注册,也是被动更新机制。
性能考虑
性能是网关一项非常重要的衡量指标,尤其是响应时间,客户端本来可以直连服务端的,现在增加了一个网关层,对于一个本身耗时几百毫秒的服务接入网关后增加几毫秒,影响倒是可以忽略不计;但如果服务本身只需要几毫秒,因为接入网关再增加一倍的延时,用户感受就会比较明显。
建议在设计上需要遵循如下原则:
核心网关子系统必须是无状态的,便于横向扩展。
运行时不依赖本地存储,尽量在内存里面完成服务的处理和中转。
减小对线程的依赖,采用非阻塞式IO和异步事件响应机制。
后端服务如果是HTTP协议,尽量采用连接池或者Http2,测试连接复用和不复用性能有几倍的差距。(TCP建立连接成本很高)
作者:王苏龙
来源:infoq
【免责声明】:本内容转载于网络,转载目的在于传递信息。文章内容为作者个人意见,本平台对文中陈述、观点保持中立,不对所包含内容的准确性、可靠性与完整性提供形式地保证。请读者仅作参考。