当前位置: 首页 > 科技观察

网络-亿级API网关如何设计?

时间:2023-03-19 00:08:31 科技观察

API网关可以看作是系统与外界通信的入口。我们可以在网关中处理一些非业务逻辑逻辑,比如授权验证、监控、缓存、请求路由等。为什么需要API网关为什么需要API网关?有几个原因:RPC协议转换为HTTP。由于我们内部开发使用RPC协议(thrift或者dubbo),暴露给内部服务,当外部服务需要使用这个接口时,往往需要将RPC协议转换为HTTP协议。请求路由。在我们的系统中,由于新旧系统使用同一个接口,所以需要根据请求上下文将请求路由到相应的接口。统一认证。由于鉴权操作不涉及业务逻辑,可以在网关层处理,不需要再到下层进行业务逻辑处理。统一监控。由于网关是对外服务的入口,我们可以在这里监控我们想要的数据,比如输入输出参数,链接时间。流控,熔断降级。对于流量控制,断路器降级非业务逻辑可以放在网关层。很多业务会自己实现一个网关层来访问自己的服务,但这对整个公司来说是不够的。统一API网关统一API网关不仅具备API网关的所有特性,还具有以下优势:统一的技术组件升级。如果公司内部有技术组件需要升级,需要跟各个业务线沟通,一般需要几个月的时间才能搞定。比如入口的安全认证存在较大的安全隐患,需要升级,如果速度还是这么慢,那肯定不行,所以有了统一网关,升级就会很快。统一服务接入。访问某项服务也很困难。比如公司开发了比较稳定的服务组件,正在大力推广。这个周期肯定很长。因为有了统一网关,统一接入只需要统一网关。节省资源。如果不同的业务、不同的部门按照我们上面的做法,都应该建立一个网关层来做这件事。可以想象,如果一个公司有100个这样的业务,每个业务配备4台机器,那么就需要400台机器。机器。而每个业务的开发RD都需要开发这个网关层,随时维护,增加人手。如果有统一的网关层,可能只需要50台机器就可以做这100个服务的网关层,业务RD不需要随时关注开发上线的步骤。统一网关设计异步请求对于自己实现的网关层,由于只是自己使用,对吞吐量要求不高,所以一般需要同步调用。对于我们的统一网关层来说,如何用少量的机器访问更多的服务,需要我们的异步来提高更多的吞吐量。异步一般有两种策略:Tomcat/Jetty+NIO+servlet3。这种策略被广泛使用。京东、有赞、Zuul都选择了这个策略。这种策略更适合HTTP。可以在Servlet3中启用异步。网络+蔚来。Netty为高并发而生。目前唯品会的网关采用的就是这种策略。在唯品会的技术文章中,同等情况下,Netty每秒吞吐量30w+,Tomcat吞吐量13w+。可以看出它们之间有一定的差距,但是Netty需要自己处理HTTP协议,比较麻烦。当网关有很多HTTP请求场景时可以使用Servlet。毕竟能更成熟的处理HTTP协议。如果更注重吞吐量,可以使用Netty。全链路异步我们已经对传入请求使用了异步,为了实现全链路异步我们需要异步处理传出请求。对于要走的请求,我们可以利用RPC的异步支持来进行异步请求。所以基本上可以实现下图:先在Web容器中开启Servlet异步,然后进入网关的业务线程池进行业务处理,然后进行RPC异步调用,注册需要回调的业务,最后在回调线程池进行回调处理。链式处理在设计模式中有一种模式叫做责任链模式。它的作用是避免请求发送方和接收方之间的耦合,让多个对象可能接收到请求,将这些对象连接成一个链,并沿着链将请求传递下去,直到一个对象处理它。这种模式将请求的发送者与请求的处理器解耦。这种模式在我们的每一个框架中都有实现,比如Servlet中的Filter,SpringMVC中的Interceptor。这种模式在NetflixZuul中也有应用,如下图所示:我们在网关的设计中可以借鉴我们自己的这种模式的网关设计:preFilters:前置过滤器,用来处理一些公共服务,比如统一认证,统一限流、断路器降级、缓存处理等,并提供业务侧扩展。routingFilters:用来处理一些广义的调用,主要是协议转换和请求路由。postFilters:后置过滤器,主要用于结果处理、日志管理、记录时间等。errorFilters:错误过滤器,用于处理调用异常。有赞的网关也采用了这种设计。在业务隔离的情况下,全链路异步情况下不同业务之间影响较小,但是如果在提供的自定义Filter中进行一些同步调用,一旦频繁超时,就会影响到其他业务。因此,我们需要采用隔离的技术来减少业务之间的相互影响。信号量隔离信号量隔离只是限制了总并发数,服务仍然是同步调用的主线程。如果远程调用超时,这种隔离还是会影响到主线程,进而影响到其他业务。因此,如果只是想限制一个服务的并发调用总数或者被调用的服务不涉及远程调用,可以使用轻量级信号量来实现。由于有赞的网关没有自定义过滤器,所以选择了信号量隔离。最简单的线程池隔离就是通过不同的线程池来隔离不同的业务。即使因为线程池已经被隔离导致业务接口出现问题,也不会影响到其他业务。在京东的网关实现中,线程池是用来做隔离的。比较重要的业务,比如商品或者订单,通过线程池单独处理。但是因为是统一网关平台,如果业务线比较多,大家觉得自己的业务比较重要,就需要单独的线程池进行隔离。如果用Java语言开发的话,线程在Java中是比较重的资源,比较有限。如果需要隔离的线程池太多,不太适合。如果使用一些其他的语言比如Golang来开发网关,线程是比较轻的资源,所以使用线程池隔离比较合适。集群隔离如果某些服务需要隔离,但是统一网关没有线程池隔离,怎么办?然后就可以使用集群隔离了。如果你的某些业务确实很重要,你可以为这一系列业务申请一个集群或者多个集群,机器之间进行隔离。很多开源的实现都可以用于请求限流和流量控制,比如阿里最近开源的Sentinel和比较成熟的Hystrix。一般限流分为集群限流和单机限流:集群限流:在使用统一存储保存当前流量时,一般可以使用Redis,一般会有一定的性能损耗。单机限流:我们可以直接使用Guava的令牌桶对每台机器进行限流,因为没有远程调用性能消耗小。熔断降级也可以参考Sentinel和Hystrix的开源实现,非重点就不提了。GeneralizationcallGeneralizationcall泛化调用是指一些通信协议的转换,比如将HTTP转换为Thrift。在Zuul等一些开源网关中并没有实现,因为每个公司内部的服务通信协议都不一样。例如,唯品会支持HTTP1、HTTP2、二进制协议,然后将其转化为内部协议。淘宝支持HTTPS、HTTP1、HTTP2,这些协议可以转换为HTTP、HSF、Dubbo等协议。如何实现广义调用?由于协议很难自动转换,实际上每个协议对应的接口都需要提供一个映射。简单的说,两个协议都可以转换成通用语言,从而相互转换,如下图所示:一般来说,指定通用语言有三种方式:json:json有一个比较简单的数据格式,解析速度快,相对轻量级。在Dubbo的生态中,有一个HTTP转Dubbo项目,使用JsonRpc将HTTP转成JsonRpc,再转入Dubbo。比如一个www.baidu.com/id=1GET可以映射到json:xml:xml数据比较重,解析难度大,这里就不展开讨论了。自定义描述语言:一般来说成本比较高,需要定义自己的语言进行描述和分析,但是其扩展性和定制性非常好。例:Spring定制了一套自己的SPEL表达式语言。广义的调用,如果你想自己设计,json基本可以满足你。如果你有很多个性化的需求,你可以自己定义一套语言。上述管理平台是网关实现的关键技术。这里需要引入一个网关的业务密钥。有了网关之后,还需要一个管理平台来配置我们上面介绍的技术关键,包括但不限于以下配置:限流、熔断缓存日志、自定义过滤器、广义调用汇总***一个合理的标准网关应该实现如下:参考资料:京东:http://www.yunweipai.com/archives/23653.html有赞网关:https://tech.youzan.com/api-gateway-in-practice/唯品会:https://mp.weixin.qq.com/s/gREMe-G7nqNJJLzbZ3ed3AZuul:http://www.scienjus.com/api-gateway-and-netflix-zuul/