个推微服务网关架构实践详解

在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求 。因此,在客户端和服务端之间增加一个API网关成为多数微服务架构的必然选择 。
在个推的微服务实践中,API网关也起着至关重要的作用 。一方面,API网关是个推微服务体系对外的唯一入口 ;另一方面,API网关中实现了很多后端服务的共性需求,避免了重复建设。
个推微服务网关的设计与实现个推微服务主要是基于Docker和Kubernetes进行实践的 。在整个微服务架构中,最底层的是个推私有部署的Kubernetes集群,在集群之上,部署了应用服务 。
个推的应用服务体系共分为三层,最上一层是网关层,接着是业务层,最下面是基础层服务 。在部署应用服务时,我们使用了Kubernetes的命名空间对不同产品线的产品进行隔离 。除了应用服务外,Kubernetes集群上还部署了Consul来实现配置的管理、Kube-DNS实现服务注册与发现,以及一些辅助系统来进行应用和集群的管理 。
下图是个推微服务体系的架构图 。

个推微服务网关架构实践详解

文章插图
 
个推对API网关的功能需求主要有以下几方面:1. 要支持配置多个产品,为不同的产品提供不同的端口;
2. 动态路由;
3. URI的重写;
4. 服务的注册与发现;
5. 负载均衡;
6. 安全相关的需求,如session校验等;
7. 流量控制;
8. 链路追踪;
9. A/B Testing 。
在对市面上已有的网关产品进行调研后,我们的技术团队发现,它们并不太适合应用于个推的微服务体系 。第一,个推配置的管理都是基于Consul实现的,而大部分网关产品都需要基于一些DB存储,来进行配置的管理;第二,大部分的网关产品提供的功能比较通用,也比较完善,这同时也降低了配置的复杂度以及灵活性;第三,大部分的网关产品很难直接融入到个推的微服务架构体系中 。
最终,个推选择使用了OperResty和Lua进行自研网关,在自研的过程中,我们也借鉴了其他网关产品的一些设计,如Kong和Orange的插件机制等 。
个推的API网关的插件设计如下图所示 。
个推微服务网关架构实践详解

文章插图
 
OpenResty对请求的处理分为多个阶段 。个推API网关的插件主要是在Set、Rewrite、Access、Header_filter、Body_filter、Log这六个阶段做相应的处理,其中,每一个插件都可以在一个或多个阶段起到相应的作用 。在一个请求到达API网关之后,网关会根据配置为该请求选择插件,然后根据每个插件的规则,进一步过滤出匹配规则的插件,最后对插件进行实例化,对流量进行相应的处理 。
个推微服务网关架构实践详解

文章插图
 
我们可以通过举例来理解这个过程,如上图所示,
localhost:8080/api/demo/test/hello这个请求到达网关后,网关会根据host和端口确定产品信息,并提取出URI(/api/demo/test/hello),然后根据产品的具体配置,筛选出需要使用的插件——Rewrite_URI、Dyups和Auth,接下来根据每个插件的规则配置进行过滤,过滤后,只有Rewrite_URI和Dyups两个插件被选中 。之后实例化这两个插件,在各个阶段对请求进行处理 。请求被转发到后端服务时,URI就被rewrite为“/demo/test/hello”,upstream也被设置为“prod1-svc1” 。请求由后端服务处理之后,响应会经网关返回给客户端,这就是整个插件的设计和工作的流程 。为了优化性能,我们将插件的实例化延缓到了请求真正开始处理时,在此之前,网关会通过产品配置和规则,过滤掉不需要执行的插件 。从图中也可以看出,每个插件的规则配置都很简单,并且没有统一的格式,这也确保了插件配置的简单灵活 。
网关的配置均为热更新,通过Consul和Consul-Template来实现,配置在Consul上进行更新后,Consul-Template会将其实时地拉取下来,然后通过以下两种方式进行更新 。
(1)通过调用Update API,将配置更新到shared-dict中 。
(2)更新配置文件,利用Reload OpenResty实现配置文件的更新 。
个推微服务网关提供的主要功能1.动态路由
动态路由主要涉及到三个方面:服务注册、服务发现和请求转发 。
如下图所示,服务的注册和发现是基于Kubernetes的Service和Kube-DNS实现的,在Consul中,会维持一个服务的映射表,应用的每一个微服务都对应Kubernetes上的一个Service,每创建一个Service都会在Consul上的服务映射表中添加一项(会被实时更新到网关的共享内存中) 。网关每收到一个请求都会从服务映射表中查询到具体的后端服务(即Kubernetes中的Service名),并进行动态路由 。Kube-DNS可以将Service的域名解析成Kubernetes内部的ClusterIP,而Service代理了多个Pod,会将流量均衡地转发到不同的Pod上 。


推荐阅读