共计 4331 个字符,预计需要花费 11 分钟才能阅读完成。
1. 背景
1.1 什么是 API 网关
API 网关能够看做零碎与外界联通的入口,咱们能够在网关进行解决一些非业务逻辑的逻辑,比方权限验证,监控,缓存,申请路由等等。
1.2 为什么须要 API 网关
- RPC 协定转成 HTTP。
因为在外部开发中咱们都是以 RPC 协定 (thrift or dubbo) 去做开发,裸露给外部服务,当内部服务须要应用这个接口的时候往往须要将 RPC 协定转换成 HTTP 协定。
- 申请路由
在咱们的零碎中因为同一个接口新老两套零碎都在应用,咱们须要依据申请上下文将申请路由到对应的接口。
- 对立鉴权
对于鉴权操作不波及到业务逻辑,那么能够在网关层进行解决,不必上层到业务逻辑。
- 对立监控
因为网关是内部服务的入口,所以咱们能够在这里监控咱们想要的数据,比方入参出参,链路工夫。
- 流量管制,熔断降级
对于流量管制,熔断降级非业务逻辑能够对立放到网关层。
有很多业务都会本人去实现一层网关层,用来接入本人的服务,然而对于整个公司来说这还不够。
1.3 对立 API 网关
对立的 API 网关不仅有 API 网关的所有的特点,还有上面几个益处:
- 对立技术组件降级
在公司中如果有某个技术组件须要降级,那么是须要和每个业务线沟通,通常几个月都搞不定。举个例子如果对于入口的平安鉴权有重大安全隐患须要降级,如果速度还是这么慢必定是不行,那么有了对立的网关降级是很快的。
- 对立服务接入
对于某个服务的接入也比拟艰难,比方公司曾经研发出了比较稳定的服务组件,正在公司大力推广,这个周期必定也特地漫长,因为有了对立网关,那么只须要对立网关对立接入。
- 节约资源
不同业务不同部门如果依照咱们下面的做法应该会都本人搞一个网关层,用来做这个事,能够设想如果一个公司有 100 个这种业务,每个业务装备 4 台机器,那么就须要 400 台机器。并且每个业务的开发 RD 都须要去开发这个网关层,去随时去保护,减少人力。如果有了对立网关层,那么兴许只须要 50 台机器就能够做这 100 个业务的网关层的事,并且业务 RD 不须要随时关注开发,上线的步骤。
2. 对立网关的设计
2.1 异步化申请
对于咱们本人实现的网关层,因为只有咱们本人应用,对于吞吐量的要求并不高所以,咱们个别同步申请调用即可。
对于咱们对立的网关层,如何用大量的机器接入更多的服务,这就须要咱们的异步,用来进步更多的吞吐量。对于异步化个别有上面两种策略:
- Tomcat/Jetty+NIO+servlet3
这种策略应用的比拟广泛,京东,有赞,Zuul,都选取的是这个策略,这种策略比拟适宜 HTTP。在 Servlet3 中能够开启异步。
- Netty+NIO
Netty 为高并发而生,目前唯品会的网关应用这个策略,在唯品会的技术文章中在雷同的状况下 Netty 是每秒 30w+ 的吞吐量,Tomcat 是 13w+, 能够看出是有肯定的差距的,然而 Netty 须要本人解决 HTTP 协定,这一块比拟麻烦。
对于网关是 HTTP 申请场景比拟多的状况能够采纳 Servlet,毕竟有更加成熟的解决 HTTP 协定。如果更加器重吞吐量那么能够采纳 Netty。
2.1.1 全链路异步
对于来的申请咱们曾经应用异步了,为了达到全链路异步所以咱们须要对去的申请也进行异步解决,对于去的申请咱们能够利用咱们 rpc 的异步反对进行异步申请所以根本能够达到下图:
由在 web 容器中开启 servlet 异步,而后进入到网关的业务线程池中进行业务解决,而后进行 rpc 的异步调用并注册须要回调的业务,最初在回调线程池中进行回调解决。
2.2 链式解决
在设计模式中有一个模式叫责任链模式,他的作用是防止申请发送者与接收者耦合在一起,让多个对象都有可能接管申请,将这些对象连接成一条链,并且沿着这条链传递申请,直到有对象解决它为止。通过这种模式将申请的发送者和申请的解决者解耦了。在咱们的各个框架中对此模式都有实现,比方 servlet 外面的 filter,springmvc 外面的 Interceptor。
在 Netflix Zuul 中也利用了这种模式,如下图所示:
这种模式在网关的设计中咱们能够借鉴到本人的网关设计:
- preFilters:前置过滤器,用来解决一些公共的业务,比方对立鉴权,对立限流,熔断降级,缓存解决等,并且提供业务方扩大。
- routingFilters: 用来解决一些泛化调用,次要是做协定的转换,申请的路由工作。
- postFilters: 后置过滤器,次要用来做后果的解决,日志打点,记录时间等等。
- errorFilters: 谬误过滤器,用来解决调用异样的状况。
这种设计在有赞的网关也有利用。
2.3 业务隔离
下面在全链路异步的状况下不同业务之间的影响很小,然而如果在提供的自定义 FiIlter 中进行了某些同步调用,一旦超时频繁那么就会对其余业务产生影响。所以咱们须要采纳隔离之术,升高业务之间的相互影响。
2.3.1 信号量隔离
信号量隔离只是限度了总的并发数,服务还是主线程进行同步调用。这个隔离如果近程调用超时仍然会影响主线程,从而会影响其余业务。因而,如果只是想限度某个服务的总并发调用量或者调用的服务不波及近程调用的话,能够应用轻量级的信号量来实现。有赞的网关因为没有自定义 filter 所以选取的是信号量隔离。
2.3.2 线程池隔离
最简略的就是不同业务之间通过不同的线程池进行隔离,就算业务接口呈现了问题因为线程池曾经进行了隔离那么也不会影响其余业务。在京东的网关实现之中就是采纳的线程池隔离,比拟重要的业务比方商品或者订单 都是独自的通过线程池去解决。然而因为是对立网关平台,如果业务线泛滥,大家都感觉本人的业务比拟重要须要独自的线程池隔离,如果应用的是 Java 语言开发的话那么,在 Java 中线程是比拟重的资源比拟受限,如果须要隔离的线程池过多不是很实用。如果应用一些其余语言比方 Golang 进行开发网关的话,线程是比拟轻的资源,所以比拟适宜应用线程池隔离。
2.3.3 集群隔离
如果有某些业务就须要应用隔离然而对立网关又没有线程池隔离那么应该怎么办呢?那么能够应用集群隔离,如果你的某些业务真的很重要那么能够为这一系列业务独自申请一个集群或者多个集群,通过机器之间进行隔离。
2.4 申请限流
流量管制能够采纳很多开源的实现,比方阿里最近开源的 Sentinel 和比拟成熟的 Hystrix。
个别限流分为集群限流和单机限流:
- 利用对立存储保留以后流量的状况,个别能够采纳 Redis,这个个别会有一些性能损耗。
- 单机限流: 限流每台机器咱们能够间接利用 Guava 的令牌桶去做,因为没有近程调用性能耗费较小。
2.5 熔断降级
这一块也能够参照开源的实现 Sentinel 和 Hystrix,这里不是重点就不多提了。
2.6 泛化调用
泛化调用指的是一些通信协议的转换,比方将 HTTP 转换成 Thrift。在一些开源的网关中比方 Zuul 是没有实现的,因为各个公司的外部服务通信协议都不同。比方在唯品会中反对 HTTP1,HTTP2, 以及二进制的协定,而后转化成外部的协定,淘宝的反对 HTTPS,HTTP1,HTTP2 这些协定都能够转换成,HTTP,HSF,Dubbo 等协定。
2.6.1 泛化调用
如何去实现泛化调用呢?因为协定很难主动转换,那么其实每个协定对应的接口须要提供一种映射。简略来说就是把两个协定都能转换成共同语言,从而相互转换。
一般来说共同语言有三种形式指定:
- json:json 数据格式比较简单, 解析速度快,较轻量级。在 Dubbo 的生态中有一个 HTTP 转 Dubbo 的我的项目是用 JsonRpc 做的,将 HTTP 转化成 JsonRpc 再转化成 Dubbo。
比方能够将一个 www.baidu.com/id = 1 GET 能够映射为 json:
代码块
{“method”: "getBaidu"
"param" : {"id" : 1}
}
- xml:xml 数据比拟重,解析比拟艰难,这里不过多探讨。
- 自定义描述语言: 一般来说这个老本比拟高须要本人定义语言来进行形容并进行解析,然而其扩展性,自定义个性化性都是最高。例:spring 自定义了一套本人的 SPEL 表达式语言
对于泛化调用如果要本人设计的话 JSON 根本能够满足,如果对于个性化的须要特地多的话倒是能够本人定义一套语言。
2.7 治理平台
下面介绍的都是如何实现一个网关的技术要害。这里须要介绍网关的一个业务要害。有了网关之后,须要一个治理平台如何去对咱们下面所形容的技术要害进行配置, 包含但不限于上面这些配置:
- 限流
- 熔断
- 缓存
- 日志
- 自定义 filter
- 泛化调用
3. 总结
最初一个正当的规范网关应该依照如上来实现:
— | 京东 | 唯品会 | 有赞 | 阿里 | Zuul |
---|---|---|---|---|---|
实现要害 | servlet3.0 | netty | servlet3.0 | servlet3.0 | servlet3.0 |
异步状况 | servlet 异步,rpc 是否异步不分明 | 全链路异步 | 全链路异步 | 全链路异步 | Zuul1 同步阻塞,Zuul2 异步非阻塞 |
限流 | — | — | 平滑限流。最后是 codis,后续换到每个单机的令牌桶限流。 | 1. 根本流控: 基于 API 的 QPS 做限流。2. 经营流控: 反对 APP 流量包,APP+API+USER 的流控 33. 大促流控:APP 拜访 API 的权重流控。阿里开源:Sentinel | 提供了 jar 包:spring-cloud-zuul-ratelimit。1. 对申请的指标 URL 进行限流(例如:某个 URL 每分钟只容许调用多少次)。2. 对客户端的拜访 IP 进行限流(例如:某个 IP 每分钟只容许申请多少次)3. 对某些特定用户或者用户组进行限流(例如:非 VIP 用户限度每分钟只容许调用 100 次某个 API 等)4. 多维度混合的限流。此时,就须要实现一些限流规定的编排机制。与、或、非等关系。反对四种存储形式 ConcurrentHashMap,Consul,Redis, 数据库。 |
熔断降级 | — | — | Hystrix | — | 只反对服务级别熔断,不反对 URL 级别。 |
隔离 | 线程池隔离 | — | 信号量隔离 | — | 线程池隔离,信号量隔离 |
缓存 | redis | — | 二级缓存,本地缓存 +Codis | HDCC 本地缓存,近程缓存,数据库 | 须要本人开发 |
泛化调用 | — | http,https,http1,http2, 二进制 | dubbo,http,nova | hsf,dubbo,http,https,http2,http1 | 只反对 http |
4. 参考
- 京东:http://www.yunweipai.com/archives/23653.html
- 有赞网关:https://tech.youzan.com/api-gateway-in-practice/
- 唯品会:https://mp.weixin.qq.com/s/gREMe-G7nqNJJLzbZ3ed3A
- Zuul:http://www.scienjus.com/api-gateway-and-netflix-zuul/