引入
微服务化进程中,可以使用如下中间件来解决服务之间通信与服务治理的问题:
- 使用RPC框架解决服务通信的问题
- 使用注册中心解决服务注册和发现的问题
- 使用分布式Trace中间件,排除跨服务调用慢请求
- 使用负载均衡器,解决服务扩展性问题
- 在API网关中植入服务熔断、降级和流控等服务治理的策略
上面这些单语言使用还行,但是在团队中经常会使用不同的开发语言。而使用不同的开发语言开发出来的微服务,在相互调用时会存在两方面的挑战。
- 一方面,服务之间的通信协议上,要对多语言友好,要想实现跨语言调用,关键点是选择合适的序列化方式。举个例子
- 比如,你用Java开发一个RPC服务,使用的是Java原生的序列化方式,这种序列化方式对于其他语言并不友好,那么,你使用其他语言,调用这个RPC服务时,就很难解析序列化之后的二进制流
- 所以,建议,在选择序列化协议时,考虑序列化协议是否对多语言友好。比如可以选择protobuf、thrift等
- 另一方面,使用新语言开发的微服务,无法使用之前累积的服务治理的策略
- 比如,RPC客户端在使用注册中心订阅服务的时候,为了避免每次RPC调度都要与注册中心交互,一般会在RPC客户端缓存节点的数据。如果注册中心中的服务节点发生了变更,那么RPC客户端的节点缓存就会得到通知,并且变更缓存数据
- 而且,为了减少注册中心的访问压力,在RPC客户端上,我们一般会考虑使用多级缓存(内部缓存和文件缓冲)来保证节点缓存的可用性。
- 而这些策略在开始是,都是使用Java 语言来实现的,并且封装在注册中心客户端里,提供给 RPC 客户端使用。如果更换了新的语言,这些逻辑就都要使用新的语言实现一套。
- 除此之外,负载均衡、熔断降级、流量控制、打印分布式追踪日志等等,这些服务治理的策略都需要重新实现,而使用其它语言重新实现这些策略无疑会带来巨大的工作量,也是中间件研发中,一个很大的痛点
那么,你要如何屏蔽服务化架构中,服务治理的细节,或者说,如何让服务治理的策略在多语言之间复用呢?
可以考虑将服务治理的细节,从RPC客户端中拆分出来,形成一个单独的代理层单独部署。这个代理层可以使用单一的语言实现,所有的流量都经过代理层,来使用其中的服务治理策略。这是一种“关注点分离”的实现方式,也是Service Mesh 的核心思想
Service Mesh 是如何工作的
什么是Service Mesh
Serviice Mesh主要处理服务之间的通信,它的主要实现方式就是在应用程序同主机上部署一个代理程序,一般来讲,我们将这个代理程序叫做“sidecar(边车)”,服务之间的通信也从从前的客户端与服务器直连,变成了下面这种形式:
在这种形式下,RPC客户端将数据包先发送给与自身同主机部署的sidecar,在sidecar中经过服务发现、负载均衡、服务路由、流量控制之后,再将数据发往指定服务节点的sidecar,在服务节点的sidecar中,经过记录访问日志、记录分布式追踪日志、限流之后,再将数据发送给RPC客户端。
这样,可以把业务代码和服务治理的策略隔离开,将服务治理策略下沉,让它成为独立的基础模块。这样一来,不仅可以实现跨语言,服务治理策略的复用,还能对这些sidecar做统一的管理。
目前,业界提及最多的 Service Mesh 方案当属istio, 它将组件分为数据平面和控制平面
- 数据平面就是Sidecar(Istio 使用Envoy作为 Sidecar 的实现)。
- 控制平面主要负责服务治理策略的执行,在 Istio 中,主要分为Mixer、Pilot 和 Istio-auth 三部分。
然而,在 Istio 中,每次请求都需要经过控制平面,也就是说,每次请求都需要跨网络的调用 Mixer,这会极大地影响性能。
因此,国内大厂开源出来的 Service Mesh 方案中,一般只借鉴 Istio 的数据平面和控制平面的思路,然后将服务治理策略做到了 Sidecar 中,控制平面只负责策略的下发,这样就不需要每次请求都经过控制平面,性能上会改善很多。
如何将流量转发到sidecar中
在service mesh的实现中,一个主要的问题就是,如何尽量无感知的引入sidecar作为网络代理。也就是说,无论是数据流入还是数据流出,都要将数据报重定向到sidecar的端口上。
主要实现方法由两个:
(1)第一个是使用iptables的方式来实现流量透明的转发
- Iptables 是 Linux 内核中,防火墙软件 Netfilter 的管理工具,它位于用户空间,可以控制Netfilter,实现地址转换的功能。
- 在 iptables 中默认有五条链,你可以把这五条链,当作数据包流转过程中的五个步骤,依次为PREROUTING,INPUT,FORWARD,OUTPUT和 POSTROUTING。数据包传输的如下图
- 从图中可以看到,数据包以 PREROUTING 链作为入口,当数据包目的地为本机时,它们也都会流经到 OUTPUT 链。所以,我们可以在这两个链上,增加一些规则,将数据包重定向。
(2)Iptables的优势在于,对于业务完全透明,业务甚至不需要有sidecar存在,这样会减少业务接入的时间。不过,它也有缺陷,那就是它是在高并发下,性能会有损耗,因此国内大厂采用了另外一种方式:轻量级客户端
- 在这种方式下,RPC客户端会通过配置的方式,知道sidecar的部署端口,然后通过一个轻量级客户端,将调用服务的请求发送给sidecar,sidecar在转发请求之前,先执行一些服务治理的策略。
- 请求被发送到服务端的 Sidecar 上后,然后在服务端记录访问日志,和分布式追踪日志,再把请求转发到真正的服务节点上。当然,服务节点在启动时,会委托服务端 Sidecar,向注册中心注册节点,Sidecar 也就知道了真正服务节点部署的端口是多少。
总结
- service mesh分为数据平面和控制平面。数据平面主要负责数据的传输;控制平面用来控制服务治理策略的植入。出于性能的考虑,一般会把服务治理策略植入到数据平面中,控制平面负责服务治理策略数据的下发
- sidecar 的植入方式目前主要有两种实现方式,一种是使用 iptables 实现流量的劫持;另一种是通过轻量级客户端来实现流量转发。