You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

73 lines
10 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 35 | 微博Service Mesh实践之路
专栏上一期我们聊了Service Mesh的代表作Istio由于Istio的设计理念非常新并且它诞生在微服务容器化和Kubernetes云平台火爆之后所以从设计和实现上Istio都天生对云原生应用更友好。
但是现实是不是也是那么美好呢对于一个已经上线运行多年的业务系统来说要想从经典的微服务架构走上Istio这条看似完美的道路并不容易各种内部基础设施的定制化以及业务稳定性优先准则等因素都注定了大多数公司要走出一条自己的Service Mesh实践之路。今天我就来带你回顾下微博是如何一步步走向Service Mesh的。
## 跨语言服务调用的需求
我在前面讲过微博的服务化框架采用的是自研的MotanMotan诞生于2013年出于微博平台业务单体化架构拆分为微服务改造的需求在结合当时的开源服务化框架和自身实际的需求选择了采用自研的方式。而且由于微博平台的业务采用的是Java语言开发所以Motan早期只支持Java语言。后期随着微博业务的高速发展越来越多的PHP业务开始登上舞台于是在微博的流量体系中主要是三股服务之间的相互调用一个是Java与Java语言一个是PHP和Java语言一个是PHP和PHP语言。Java应用之间的调用采用的是Motan协议而Java应用与PHP、PHP与PHP应用之间采用的都是HTTP协议。我回忆了一下当时一次PHP与Java之间的HTTP调用过程大致需要经过DNS解析、四层LVS负载均衡、七层Nginx负载均衡最后才能调用Java应用本身。
![](https://static001.geekbang.org/resource/image/8c/04/8cafb3bc4c314e5df40a80f8aee3cc04.png)
从上面这张图可以看出一次HTTP调用的链路相当长从我的实践来看经常会遇到好几个问题。
**第一个问题:中间链路损耗大**。由于一次HTTP调用要经过DNS、LVS、Nginx这三个基础设施每一层都会带来相应的损耗。我曾经在线上就碰到过因为DNS解析延迟、LVS带宽打满引起的网络延迟以及Nginx本地磁盘写满引起的转发延迟等各种情况造成接口响应在中间链路的损耗甚至超过了接口本身业务逻辑执行的时间。
**第二个问题:全链路扩容难**。由于微博业务经常要面临突发热点事件带来的流量冲击,所以需要能够随时随地动态扩缩容。其实在应用本身这一层扩容并不是难点,比较麻烦的是四七层负载均衡设备的动态扩缩容,它涉及如何评估容量、如何动态申请节点并及时修改生效等,要完成一次全链路扩容的话,复杂度非常高,所以最后往往采取的办法是给四七层负载均衡设备预备足够的冗余度,在峰值流量到来时,只扩容应用本身。
**第三个问题:混合云部署难**。专栏前面我讲过微博的业务目前采用的是混合云部署也就是在内网私有云和公有云上都有业务部署同样也需要部署四七层负载均衡设备并且要支持公有云上的请求经过DNS解析后要能够转发到公有云上的负载均衡设备上去避免跨专线访问带来不必要的网络延迟和专线带宽占用。
因此迫切需要一种支持跨语言调用的服务化框架使得跨语言应用之间的调用能够像Java应用之间的调用一样不需要经过其他中间链路转发做到直接交互就像下图描述的那样。
![](https://static001.geekbang.org/resource/image/6b/fe/6b54bcf17039645c3034dce373a053fe.png)
## Yar协议的初步尝试
为此微博最开始考虑基于Motan框架进行扩展使其支持PHP语言的Yar协议下面是扩展后的架构图。这个架构的思路是PHP客户端的服务发现通过Nginx来支持经过Nginx把PHP的Yar协议请求转发给服务端由于Motan框架中了适配Yar协议服务端会把PHP的Yar协议请求转换成Motan请求来处理处理完后再转成Yar协议的返回值经过Nginx返回给客户端。
![](https://static001.geekbang.org/resource/image/d7/3c/d7d21afa6d37bf5f55a831a25fdef83c.png)
但这种架构主要存在两个问题。
**第一个问题**Motan协议与Yar协议在基本数据结构和序列化方式的支持有所不同需要经过复杂的协议转换。
**第二个问题**服务调用还必须依赖Nginx所以调用链路多了一层在应用部署和扩容时都要考虑Nginx。
## gRPC会是救命稻草吗
时间往后推演gRPC横空出世它良好的跨语言特性以及高效的序列化格式的特性吸引了我们于是便开始考虑在Motan中集成gRPC来作为跨语言通信的协议。当时设计了下图的架构这个架构的思路是利用gRPC来生成PHP语言的Client然后在Motan框架中加入对gRPC协议的支持这样的话PHP语言的Client就可以通过gRPC请求来调用Java服务。
![](https://static001.geekbang.org/resource/image/02/2a/02de374239ba3b0ea10cc9192821552a.png)
但在我们的实际测试中发现微博的业务场景并不适合gRPC协议因为gRPC协议高度依赖PB序列化而PHP对PB的兼容性不是很好在微博的业务场景下一个接口返回值有可能超过几十KB此时在PHP Client端PB数据结构解析成JSON对象的耗时甚至达到几十毫秒这对业务来说是不可接受的。而且gRPC当时还不支持PHP作为Server对外提供服务也不满足微博这部分业务场景的需要。
## 代理才是出路
考虑到PHP语言本身没有常驻内存控制的能力在实现服务注册和发现以及其他各种服务框架功能时仅靠PHP-FPM进程本身难以实现因此需要一个统一常驻内存的进程来帮助完成服务框架的各种功能。一开始我们考虑过使用本地守护进程和OpenResty的Timer来实现服务发现但其他服务框架的功能不太好实现比如专栏前面提到的各种复杂的负载均衡策略、双发、熔断等。为此我们希望通过一个Agent也就是代理来帮助PHP进程来完成服务框架的各种功能PHP进程本身只需要负责运行业务逻辑的代码以及最简单的Motan协议解析。基于这个思路当时我们设计了下面这个架构它的思路就是在PHP进程的本地也部署一个AgentPHP进程发出去的请求都经过Agent进行处理后再发给对应的Java应用。
![](https://static001.geekbang.org/resource/image/a7/5c/a75e0cd4b3e9aa355a6caec951e5845c.png)
## 向Service Mesh迈进
2017年就在我们开始采用Agent方案对业务进行改造以支持PHP应用调用Java应用服务化的时候Service Mesh的概念突然火热起来并随着Istio的发布风靡业界。相信经过我前面对Service Mesh的讲解你一定会发现这里的Agent不恰恰就是Service Mesh中的SideCar吗没错我们跨语言调用的解决方案竟然与Service Mesh的理念不谋而合。借鉴Service Mesh的思想我们也对Agent方案进一步演化不仅客户端的调用需要经过本地的Agent处理后再转发给服务端服务端在处理前也需要经过本地的Agent最后再由服务端业务逻辑处理下面是它的架构图。如此一来业务只需要进行集成最简单的Motan协议解析而不需要关心其他服务框架功能可以理解为业务只需要集成一个轻量级的Client用于Motan协议解析而繁杂的服务框架功能全都由Agent来实现从而实现业务与框架功能的解耦。
![](https://static001.geekbang.org/resource/image/78/91/78c53f81b72dc818d90400160b573d91.png)
从上面的图中你可以看出这个架构与上一期我们聊的Istio大体思路相同但是区别还是很明显的可以概括为以下几点
* 都通过SideCar方式部署的代理来实现流量转发Istio里使用的是Envoy而Weibo Mesh采用的是自研的Motan-go Agent。这里有一个很明显的区别是Weibo Mesh中业务代码还需要集成一个轻量级的Client所以对业务有一定的倾入性而Istio采用的是iptables技术拦截网络请求给Envoy所以业务无需做任何变更更适合云原生应用。在微博的业务场景下由于大部分业务并不是云原生应用都是部署在物理机或者虚拟机集群之中的所以需要根据自己的业务特点来决定SideCar的部署方式。而且Weibo Mesh中的轻量级Client除了实现基本的Motan协议的解析功能之外还添加了一些业务需要的特性比如为了防止Agent不可用在本地保存了一份服务节点的本地快照必要时Client可以访问本地快照获得节点的地址直接向服务节点Server发起调用而不需要经过Agent转发处理只不过这个时候就丧失了Agent的服务治理功能。
* Weibo Mesh和Istio都具备服务治理功能只不过Istio是通过Control Plane来控制Proxy来实现并且Control Plane包括三个组件Pilot、Mixer以及Citedar三者各司其职。而Weibo Mesh是通过统一的服务治理中心来控制Agent从而实现服务治理的。这是因为微博本身的各种基础设施大部分是自研的比如注册和配置中心是自研的Vintage、监控系统是自己基于Graphite改造的、容器平台DCP以及负责容量评估的Diviner也是自研的为此需要一个统一的地方把这些基础设施串联起来。而Istio好像就为开源而生设计之初就要考虑如何更好地集成并支持各类开源方案比如专门抽象出Mixer组件来对接各种监控和日志系统。
## 总结
今天我给你讲解了微博是如何一步步走向Service Mesh之路的从这个过程你可以看出微博的Weibo Mesh并不是一开始就是设计成这样的它是随着业务的发展对跨语言服务调用的需求日趋强烈才开始探索如何使得原有仅支持Java语言的服务化框架Motan支持多语言在这个过程中又不断尝试了各种解决方案之后才笃定了走Agent代理这条路并实际应用到线上。而随着Service Mesh概念的兴起微博所采用的Agent代理的解决方案与Service Mesh理念不谋而合于是在Agent代理的方案中吸纳Service Mesh的思想进一步演变成如今的Weibo Mesh。所以说一个可靠的架构从来都不是设计出来的是逐步演进而来的。
## 思考题
如果要支持更多不同语言应用之间的相互调用你觉得Weibo Mesh中的轻量级的Client需要做哪些工作
欢迎你在留言区写下自己的思考,与我一起讨论。