API网关:API 网关从入门到放弃
前言
假设你正在开发一个电商网站,那么这里会涉及到很多后端的微服务,比如会员、商品、推荐服务等等。
那么这里就会遇到一个问题,APP/Browse7 ] v S Pr怎么去访问这些后端的服务? 如果业务比较简单的话,可以给每个业务都d ? Y Z 6 ` ( x分配一个独立的域` C ] - 4 4 3 !名(http://service.api.company.com),但这C I . P t Q q O Y种方式会有几个问题:
- 每个业务都会l a ^ ) c t需要鉴权、限流、权限校验等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,& / U , I d 会很蛋疼,完 o | 6全可以抽} K @出来,放到一个统一的地方去做j g s。
- 如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能& 9 ;会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名,另一方面是连接数的瓶颈,想象一下R - y你打开一个APP,u + 通过抓包发现涉及, L ] ) 8 &到了数百个远程 h 4 5 g D调用,这在移动端下会显得非常低效。
- 每上线一个新的服务,都需要运维参与,申& w 1 g P , z : (请域名、配置Nginx等,当上线、下线服务器时,同u h } u s !样也需要运维参与,另外采用域名这种方式,对于环境的隔离也不太友好,调用者需要自己根据域名 D 5 X自己进行判断。
- 另外还有一个问题r x 3 y W $ l yI等,比如阿里云的网关:
协议转换
内部的API可能是由很多种不同的协议实现的,比如HTTP、Dubbo、GRPC等,但对于用户来说其中很多都不是很友好,或者根本没法对外暴露,比如Dub_ J l n R r 3 I个域名,这时候预发的网关调用的就是线上的T V o L b i 2服务。
服务调用
网关由于对接很多种不同的协议,因此可能需要实现很多种调用方式,比如HTTP、Dubbo: Q r等,基于性能原因,最好都采用异步的方式,而Http、Dubbo都是支持异步的,比如apache就提供了基于NIO实现的异步HTTP客户端。
因为网关会涉及到很多异步调用,比如拦截器k O ( L / ~ c、HTTP客户端、dubbo、redis等,因此需要考虑下异步调用的方式,如果] ; y ~ n 9 Y .基于回调或者future的话,代码嵌套会很深,可读性很差,可以参考zuul和spring cloud gateway的方案,基于响应式进行改造。
优雅下线
优雅下线也是网关需要关注的一个问题,网关底层会涉及到很多种协议,比如7 B M ` & VHTTP、Dubbo,而HTTP又可以继续细分,比如域名、注册中心等,有些自身就支持优雅下线,比如Nginx自身是支持健康监测机制的,如果检测到某一个节点已经挂掉了,就会把这个节点摘掉,对于应用正常下线,需要结合发布系统,首先进行逻辑下线,然后对后续Nginx的健康监测g y m o M请求直接返回失败(比如直接返回500),然后等待一段时9 C v F h f ?间(根据Nginx配置决定),然后再将应用实A ) 0 j o际下线掉。另外对于注册中心的其实也类似,一般注册中心是只支持手动` = 下线的,可以在逻辑下线阶段调用注册中心的接口将节点下线掉,而有些不支持主动下线的,需要结合缓存的配置,让应用延迟下线。另外对于其他比如Dubbo等原理也是类似。
性能
网关作为所有流量的入口,性能是重中之重,早期大部分网关都是基于同步阻塞模型构建的,比如Zuul 1.x。但这种同步的模型我们都知道` B O P ! 1,每个请求/连接都会占用一个线程,而线程在JVM中是一个很重的资源,比如Tomcat默认就是200个线m ( 4 { Q v O G程,如果网关隔离没有做好的话,当发生网络延迟、FullGC、第三方服务慢等情况造成上游服务延迟时,线程池很容易会被打满,造成新的请求被拒绝,但这个时候其实线程都阻塞在IO上,系统f d P : Z B t的资源被没有得到充分的利用。另外一点,容易受% y ^ h V 9网络、磁盘IO等延迟影响。需要谨慎设f V k T C置超时时间,如果设置不当,且服务隔离做的不是很完善的话,网关很容易被一个慢接口拖垮# J N S ,。
而异步化的方式则完全G U k y 8 A v T不同,通常情况下一个CPU核启动一个线程即W W W =可处理所有的请求、响应。一个请求的生2 B V . 3 J o i命周期不再固定于一个线程4 b 0,而是会分成不同的阶段交由不同的线程池处理,系统的资源能够得到更充分的利用。而且因为线w { c n w程不再被某一个连接独占,一个连接所占用的系统资源也会4 W t q y e D q低得多,只是一个文件描述符加上5 Z _几个监听器等N 0 m h,而在阻塞模型中,每条连接都会独占一个线程,而线程是一个非常重的资源。对于上游服务的延迟情况,也能够得到很大的缓解,t W R F ,因为在阻塞模型中,慢请求会独占一个线程资源,而异步化之后,因为单条连接所o 6 E C $ v W占用的资源变的非常低,系统可以同时处理大量的请求。
如果是JVM平台,Zuul 2、Spring Cloud gateway等都是不错的异步网关选型,另外也可以基于Netty、Spz ( Rring Boot2.x的webflux、vert.x或者servlet3.1的异步支持进行自研。
缓存
对于一些幂等的get请求,可以在网关层面根据业务方指定的缓存头做一层缓存,存储到Redis等二级缓存中,这样一些重复的请求,可以在网关层直接处理,而不用打到业务线,降低业务方的9 y . x k N压力,另外如果业务方节点挂掉,网关也能够返回自身的缓存。
限流
限流对于每个业务组件来说,可以说都是一个必须的组件,如果限t u I R ! 9 t @流做不好的话,当请求量突z b 4 H增时,很容易导致业务方的服务挂掉,比如双11、双12等大促时,接口的请求量是平时的数倍,如果没有评估好容量,又没有做限流的话,很容易服务整个不可用,因此需要根据业务方接口的处理能力,做好限流策略,相信大家都见过淘宝、百度抢红包时的降级页面。
因此一定要在接入层做好限流策略,对于非核心c y s y [ P / #者加日志排查问题时,不可避免的需要重新发布,因此可以参考zuul的方式,将所有的核心功能都基于不同的拦截器实现,拦截器的代码采用Groovy编写,存储到数据库中,支W @ j K 持动态加载、编译、运行,这样在出了问题的时候能够s R # 5第一时间定位并解决,并且如果网关需要S , r $ }开发新功能,只需要增加新的拦截器,并动态添加到网关即可,不需要重新发布。4 Q z =
熔断降级
熔断机制也G } B f _ O { K是非常重要的一项。w t ( v ! h若某一个服务挂掉、接口响应严重超时等发生,则可能整个网关都被一个接口拖垮,因此需要增加熔断降级6 . f R j / 6,当发生D W E P 5 x z N e特定异常的时候,对接口降级由网关直接返回,可以基于Hystrix或者Resilience4j实现。
日志
由于所有) H r V / A v A的请求都是由网关处理的,因此日志也需要相对比较完善,比如接口的耗时、请求方式、请求F ( 4 ` y J Q W IP、请求参数、响应参数(注意脱敏)等,另外由于可V M m ) U - w e能涉及到很多微服务,因此需要提供一个统一的trat z O - D z 8 FceId方便关联所有的日志,可以将这个tR u 3raceId置于响E j : N # | = V应头中,方便排查问题。
隔离
比如线程池、http连接池、redis等应用层面的隔离,另外也可以根据业务场景,将核心业务部署带单独的网关集群,与其他非核心业务隔离开。
网关管控平台
这块也是非常重要的一环,需要考虑好整个流程的用户体验,比如接入到网关的这个流程,能不能尽量简化、智能,比如如果是dubbo接口,我们可以通过到git仓库中3 Z : C c获取源码、解析对应^ 2 l Y ;的类、方法,从而实现自c 2 j l ; # / C S动填充,尽量帮` A B _ V H用户减少操作;另外接口一般是从测试->预发->线上,如果每次都要填写一遍表单会非常麻^ o %烦,我们能不能自动把这个事情做掉,另外如果网u p e A q F } P关部署到了多个可用区、甚至不同的国家,那这个时候,我们还需要接口数据同步功能,不然用户需要到每个后台都操作一遍,F N e * w非常麻烦。
这块个人的建议是直接_ u g } C D 9参考阿里云、aws等提供的网关服务即可,功能非常全面。
其他
其他还有些需要考虑到的点,比如接口mock,文档生成、sdk代码生成、错误码统一、服务治理相关的等,这里就不累述 d 5 = ; 5 q了。
总结
目前的网关还是$ $ ~ U # z中心化的架k $ R p c k x构,所有的请求都需要走一次网关,因此当大促或者流量突增时,网关可能会成为性能的瓶颈,而且当网关接入的大量接口的时候,做好流量评估也不是一项容易的工作,每次大促前都需` B Z o & s E l d要跟业务方一起针对t @ d ^ [接口做压测,评估出大致的容量,并对网关进行扩容,而且网关是所有流量的入口,所有的请求都是由网关处理,要想准确的评估出容量很复杂。可以参考目前比较流行的ServiceMesh,采用去中心化的方案,将网关的逻辑下沉到sit g w } E f O 6 [decar中,sidecar和应用部署到同一个节点,并接管应用流W T e P ; L m r N D d 1 7外升级也q l a A ! g $会更平滑,中心化的网关,即使灰度发布,但是理论上所有业务方的流量都会流入到新版本的网关,如果出了问题,会影响到所有的业务,但这种去中心化的O z X方式,可以先针对非核心业务升级,观察一段时间没问题后,再全量推上线。另外ServiceMesh的方案,对于多语言支持也更友好。
本Q W P z ] e Z文转载于公众号: java思维导图
作y n A s者:aCoder2013
github.com/aCoder2013/blog/issues/35