微博微服务架构的Service Mesh实践之路

胡忠想

微博技术专家,2012年加入微博,一直在做微博首页信息流相关的业务研发。

微博@古月中心相心



自我介绍

大家好,我是胡忠想,微博技术专家。从2012年加入微博到现在,一直在做微博首页信息流相关的业务研发。


这期间不仅参与了微博后端架构从大的单体应用迁移到微服务架构的改造,还作为主要负责人之一,主导了微服务架构在公司多个业务线的推广和落地,很荣幸在这里可以跟大家分享下微博在微服务架构方面的实践经验。


01

前言

_____

说到Service Mesh,在如今的微服务领域可谓是无人不知、无人不晓,被很多人定义为下一代的微服务架构


Service Mesh在诞生不到两年的时间里取得令人瞩目的发展,在国内外都涌现出一批具有代表性的新产品,最著名的莫过于GoogleIBM领导的Istio,也是Service Mesh技术的代表之作。


而国内在这一方面也不遑多让,秉承了Service Mesh的思想也走出了各自的实践之路,并且已经开始在线上的核心业务中大规模使用,比如微博的Weibo Mesh华为公有云Service Mesh以及蚂蚁金服的SOFA Mesh等。


今天我就来带给大家详细介绍下微博的微服务架构是如何一步步走向Service Mesh之路的


02

跨语言服务调用的需求

_____

微博最早服务化框架采用的是自研的Motan,Motan诞生于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应用本身。



从上面这张图可以看出,一次HTTP调用的链路相当长,从我的实践来看,经常会遇到好几个问题。


第一个问题:中间链路损耗大


由于一次HTTP调用要经过DNS、LVS、Nginx这三个基础设施,每一层都会带来相应的损耗。


我曾经在线上就碰到过因为DNS解析延迟、LVS带宽打满引起的网络延迟,以及Nginx本地磁盘写满引起的转发延迟等各种情况,造成接口响应在中间链路的损耗甚至超过了接口本身业务逻辑执行的时间。


第二个问题:全链路扩容难


由于微博业务经常要面临突发热点事件带来的流量冲击,所以需要能够随时随地动态扩缩容。


其实在应用本身这一层扩容并不是难点,比较麻烦的是四七层负载均衡设备的动态扩缩容,它涉及如何评估容量、如何动态申请节点并及时修改生效等,要完成一次全链路扩容的话,复杂度非常高。


所以最后往往采取的办法是给四七层负载均衡设备预备足够的冗余度,在峰值流量到来时,只扩容应用本身。


第三个问题:混合云部署难。 


微博的业务目前采用的是混合云部署,也就是在内网私有云和公有云上都有业务部署,同样也需要部署四七层负载均衡设备。


并且要支持公有云上的请求经过DNS解析后要能够转发到公有云上的负载均衡设备上去,避免跨专线访问带来不必要的网络延迟和专线带宽占用。


因此,迫切需要一种支持跨语言调用的服务化框架,使得跨语言应用之间的调用能够像Java应用之间的调用一样,不需要经过其他中间链路转发,做到直接交互,就像下图描述的那样。



03

Yar协议的初步尝试

_____

为此,微博最开始考虑基于Motan框架进行扩展,使其支持PHP语言的Yar协议,下面是扩展后的架构图。



这个架构的思路是PHP客户端的服务发现通过Nginx来支持经过Nginx把PHP的Yar协议请求转发给服务端


由于Motan框架中了适配Yar协议,服务端会把PHP的Yar协议请求转换成Motan请求来处理,处理完后再转成Yar协议的返回值经过Nginx返回给客户端。


但这种架构主要存在两个问题


第一个问题:Motan协议与Yar协议在基本数据结构和序列化方式的支持有所不同,需要经过复杂的协议转换。


第二个问题:服务调用还必须依赖Nginx,所以调用链路多了一层,在应用部署和扩容时都要考虑Nginx。


04

gRPC会是救命稻草吗

_____

时间往后推演,gRPC横空出世,它良好的跨语言特性,以及高效的序列化格式的特性吸引了我们,于是便开始考虑在Motan中集成gRPC,来作为跨语言通信的协议。


当时设计了下图的架构,这个架构的思路是利用gRPC来生成PHP语言的Client然后在Motan框架中加入对gRPC协议的支持,这样的话PHP语言的Client就可以通过gRPC请求来调用Java服务。



但在我们的实际测试中,发现微博的业务场景并不适合gRPC协议,因为gRPC协议高度依赖PB序列化,而PHP对PB的兼容性不是很好,在微博的业务场景下一个接口返回值有可能超过几十KB。


此时在PHP Client端PB数据结构解析成JSON对象的耗时甚至达到几十毫秒,这对业务来说是不可接受的。


而且gRPC当时还不支持PHP作为Server对外提供服务,也不满足微博这部分业务场景的需要。


05

代理才是出路

_____

考虑到PHP语言本身没有常驻内存控制的能力,在实现服务注册和发现以及其他各种服务框架功能时,仅靠PHP-FPM进程本身难以实现,因此需要一个统一常驻内存的进程来帮助完成服务框架的各种功能


一开始我们考虑过使用本地守护进程OpenResty的Timer来实现服务发现,但其他服务框架的功能不太好实现,比如专栏前面提到的各种复杂的负载均衡策略、双发、熔断等。


为此,我们希望通过一个Agent也就是代理,来帮助PHP进程来完成服务框架的各种功能,PHP进程本身只需要负责运行业务逻辑的代码,以及最简单的Motan协议解析。


基于这个思路,当时我们设计了下面这个架构,它的思路就是在PHP进程的本地也部署一个Agent,PHP进程发出去的请求都经过Agent进行处理后,再发给对应的Java应用。



06

向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来实现,从而实现业务与框架功能的解耦。


07

Motan-go Agent

_____

考虑到Motan-go Agent要与PHP进程部署在一起,为了减少对本机资源的占用,这里Motan-go Agent采用了Go语言来实现,它包含的功能模块请看下图。



我们拆解一下图中Motan-go Agent主要的模块,看看它们的作用是什么。


Filter Chain模块是以请求处理链的组合方式,来实现AccessLog(请求日志记录)、Metric(监控统计)、CircuitBreaker(熔断)、Switcher(降级)、Tracing(服务追踪)、Mock(单元测试)、ActiveLimit(限流)等功能。



High Available模块是用来保证高可用性,默认集成了Failover、Backup Request等故障处理手段。


Load Balance模块负载均衡,默认集成了Random、Roundrobin等负载均衡算法。


EndPoint模块的作用是封装请求来调用远程的Server端,默认可以封装Motan请求和gRPC请求。


Serialize模块负责实现不同类型的序列化方式,默认支持Simple序列化。


Server模块实现不同类型的Server,要么是采用Motan协议实现,要么是采用gRPC协议。


Motan-go Agent每个模块都是功能可扩展的,你可以在Filter Chain模块加上自己实现的Trace功能,这样请求在经过Filter Chain处理时,就会自动加载你加上的Trace功能。


当然,你也可以在High Available模块添加自己实现的故障处理手段,在Load Balance模块里实现自己的负载均衡算法,在EndPoint模块封装HTTP协议的请求,在Serialize模块添加PB序列化,在Server模块实现HTTP协议等。


另外Motan-go Agent之间的通信采用的是自定义的Motan2协议,它把请求中的Meta信息与请求参数信息进行了分离,更适合对请求进行代理转发,并且默认使用了Simple序列化来对不同语言的数据进行编码,以实现跨语言服务通信。


更多关于Motan2协议和Simple序列化的介绍,你可以点击这里查看。


08

统一服务治理中心

_____

在Weibo Mesh中是通过统一服务治理平台与Motan-go Agent交互来实现服务治理功能的。


对着下面这张Weibo Mesh的架构图,我们一起看一下统一服务治理平台SGCenter具体是如何与Motan-go Agent交互,来实现服务治理的各项功能的。



1.动态服务注册与发现

首先来看下统一服务治理平台是如何实现服务注册与发现的。


如下图所示,在Motan-go Agent中实现了具体的服务注册与发现的逻辑,Server端进程启动时,会通过Motan-go Agent向Vintage注册中心发起注册请求,把服务注册到Vintage中。


Client端发起服务调用时,会经过Motan-go Agent转发,Motan-go Agent会调用Vintage查询该服务在Vintage中的注册信息,获取到服务节点列表后,按照某一种负载均衡算法选择一个服务节点,向这个服务节点发起调用。


可以通过统一服务治理平台SGCenter,调用Vintage的管理接口,执行添加或者删除服务节点等操作,Motan-go Agent会感知到服务节点的变化,获取最新的服务节点。


一般在业务开发或者运维人员需要手工扩容或者缩容一批服务节点时,才会执行这个操作。



2.监控上报

再看下面这张图,Client端发起的请求经过Motan-go Agent转发时,Motan-go Agent就会在内存中统计每一次调用的耗时、成功率等信息。


并且每隔固定的时间间隔将这段时间内各个服务调用的QPS、平均耗时、成功率以及P999等metric信息发送给Graphite监控系统。


这样的话,通过SGCenter调用Graphite的Web API就可以获取到服务调用的信息了。



3.动态流量切换与降级

动态流量切换与降级的过程请看下面这张图。


Motan-go Agent在查询Vintage中某个服务节点信息的同时也会订阅该服务的变更,这样的话就可以通过SGCenter向Vintage下发服务的切流量或者降级指令。


订阅了这个服务的Motan-go Agent就会收到变更通知,如果是切流量指令,比如把调用永丰机房服务的流量都切换到土城机房,那么Motan-go Agent就会把原本发给永丰机房的请求都发给土城机房。


如果是降级指令,Motan-go Agent就会停止调用这个服务。



4.自动扩缩容

服务调用时Motan-go Agent会把Server端服务调用的监控信息上报给Graphite监控系统,同时Diviner容量评估系统会实时调用Graphite以获取服务在不同区间的QPS信息以计算服务池的水位线。


然后SGCenter会每隔一段时间调用Diviner来获取各个服务池的冗余度以决定是否需要扩容。


假如此时服务池的冗余度不足的话,SGCenter就会调用DCP容器运维平台给服务池进行扩容,DCP完成扩容后新的服务节点就会注册到Vintage当中,这样的话订阅了该服务的Motan-go Agent就会感知到服务节点的变化。


从Vintage中获取最新的服务节点信息,这就是一个服务自动扩缩容的整个流程,你可以参考下面这张图。



09

Weibo Mesh的收益

_____

经过前面的讲解,相信你已经对Weibo Mesh的实现方案有了一定的了解。Weibo Mesh是在微博的业务场景下,一步步进化到今天这个架构的,它给微博的业务带来的巨大的收益,总结起来主要有以下几点:


  • 跨语言服务化调用的能力



    Weibo Mesh发展之初最首要的目的,就是想让微博内部的Motan服务化框架能够支持PHP应用与Java应用之间调用,因而开发了Motan-go Agent,并在此基础上演变成今天的Weibo Mesh。




    支持多种语言之间的服务化调用,有助于统一公司内部业务不同语言所采用的服务化框架,达到统一技术体系的目的。


  • 统一服务治理能力


    以微博应对突发热点事件带来的峰值流量冲击为例,为了确保首页信息流业务的稳定性,我们有针对性的研发了自动扩缩容系统。


    而随着微博的不断发展,不断涌现出新的业务线,比如热门微博和热搜,也同样面临着突发热点事件带来的流量冲击压力。


    而开发一套稳定可用的自动扩缩容系统并非一朝一夕之事,如何能够把信息流业务研发的自动扩缩容系统推广到各个业务线,是个比较棘手的问题。


    因为信息流业务的后端主要采用了Java语言实现,而热门微博和热搜主要采用的是PHP语言,无法直接接入自动扩缩容系统。


    而Weibo Mesh可以支持多种语言,将热门微博和热搜业务进行服务化改造,就可以统一接入到自动扩缩容系统,实现了公司级的统一服务治理能力。



  • 业务无感知的持续功能更新能力


    采用Motan或者Dubbo类似的传统服务化框架,一旦服务框架功能有升级就需要业务同步进行代码升级,这对大部分业务来说都是一种不愿承受的负担。


    而采用Weibo Mesh,添加新功能只需要升级Motan-go Agent即可,业务代码不需要做任何变更,对于业务开发人员更友好。


    尤其是作为公司级的服务化框架时,服务框架的升级如果跟业务系统升级绑定在一起,从我的实践经验来看,将是一件耗时费力的工作,需要协调各个业务方配合才能完成。


    而Weibo Mesh可以看作是服务器上部署的基础组件,它的升级与维护不需要各个业务方的参与,这样才能具备作为公司级的服务化框架推广到各个业务线的前提。



10

Weibo Mesh的发展规划

_____

在微博的业务场景下,存在大量服务对缓存、数据库以及消息队列等资源的调用,如果把资源也看作是一种服务


那么Weibo Mesh不仅可以管理服务与服务之间的调用,还可以管理服务与资源之间的调用,这样的话Weibo Mesh强大的服务治理能力也能延伸到对资源的治理上,对业务来说又将解决资源治理这一大难题。


另一方面,随着Weibo Mesh治理的服务越来越多,收集的数据也越来越多,利用这些数据可以挖掘一些更深层次的东西,也是Weibo Mesh未来的发展方向之一。


比如,引入机器学习算法,对采集的数据进行分析,进行监控报警的优化等。



11

总结

_____

从微博一步步走向Service Mesh之路的过程,你可以看出微博的Weibo Mesh并不是一开始就是设计成这样的,它是随着业务的发展,对跨语言服务调用的需求日趋强烈,才开始探索如何使得原有仅支持Java语言的服务化框架Motan支持多语言。


在这个过程中又不断尝试了各种解决方案之后,才笃定了走Agent代理这条路,并实际应用到线上。


而随着Service Mesh概念的兴起,微博所采用的Agent代理的解决方案与Service Mesh理念不谋而合,于是在Agent代理的方案中吸纳Service Mesh的思想,进一步演变成如今的Weibo Mesh。所以说一个可靠的架构从来都不是设计出来的,是逐步演进而来的。


以上内容均节选自我在极客时间的专栏《从0开始学习微服务》中的章节,有对微博的微服务实践感兴趣的,欢迎订阅专栏详细了解,我会在专栏下的留言区与你一起探讨微服务的最佳实践。


推荐一个压箱底的课程,如图。


往期推荐

       ……


技术琐话 


以分布式设计、架构、体系思想为基础,兼论研发相关的点点滴滴,不限于代码、质量体系和研发管理。本号由坐馆老司机技术团队维护。