明星分分合合的洪荒点击量,微博Mesh服务化改造如何支撑?(附PPT下载)

阅读:435 2019-03-19 14:41:10 来源:新网

大家好,我今天的分享主要围绕以下几点,首先跟大家简要介绍一下微博服务化的演进过程,其次是微博自研跨语言rpc框架motan实现的一些关键技术要点,主要是跨语言方面,再次,结合目前市面上的一些servicemesh实现对比,给出基于motan-go的更符合微博场景的weibomesh实现。

最后,是我个人对于面向未来泛服务化架构的一些思考。一些同学对servicemesh比较感兴趣,也想在生产上做一些实践,如果没有历史包袱,新开发一个项目用什么架构,怎么实现都是可以的。由架构去取舍,看我们更迫切需要的是什么,所追求的是性能还是其它高扩展性等等,目前也有一些现成的解决方案。但是如果没有做任何服务化,或者服务化过程中历史包袱比较重,可能很难找到一种可以直接实施的现成方案,因为需要考虑的往往会更多。本次分享更多是关于操作性内容,希望对大家有一定的借鉴意义。

微博平台服务化演进

我们先看微博平台服务化的演进过程。

微博2009年上线,在业务高速发展的初期是传统的模块化单体服务,每个小的业务都有可能随时爆发成长为一个核心业务。我们的目标很简单,就是满足业务快速发展的需求,并且在突发流量增长时保证服务的高可用。随着业务的扩张,单体架构各个模块严重耦合,致使相互的影响越来越大,请求成功率得不到保障,长尾问题严重。为了解决这一系列问题,微博从2013年开发了java语言的motanrpc框架,并基于此完成了服务化改造。motan从2013年上线至今经历过每次热点事件、三节高峰的严峻考验,稳定性和可靠性都得到了实际场景的验证。这些经历之下微博motan也积累了一套服务治理型rpc的服务化体系。

除了motan,2015年开始,为了应对越来越猛的流量洪峰,更合理的对资源进行整合利用,开发了opendcp弹性云计算平台。实现了动态的弹性扩缩容,告别了以往花费动辄几千万的资源成本,以及节前几个月就要开始的劳民伤财为三节做准备的日子,也不需要在为了一个个的热点事件提心吊胆。

上图是当时微博平台技术体系的概貌,是一个基于opendcp弹性云计算平台和motanrpc的服务化架构,经过几年的运营和考验,我们已经在混合云的服务化方向有了丰富的经验和积累。但在解决了微博平台内部服务高可用、高性能的问题后,业务方平台服务调用的问题开始显现出来。比如调用链路过长,导致坑多性能差,又比如,每个业务方都需要做高可用和服务治理,而这些事情平台已经有丰富的积累,大家都再做一遍特别重复且浪费。当想把在平台上的技术积累服务到微博更多的业务方时,平台内部基于java的服务化体系已经不能完成任务,所以我们需要一个跨语言的解决方案。从2016年起,我们开始了跨语言服务化改造之路。

跨语言rpc要点

异构系统如何做整体服务化的解决方案,也是今天探讨的一个主题。

服务化经历了前面几个阶段后,需要构建一个跨语言的服务化体系,将原来在java体系积累的经验加以总结,给其它的技术栈业务赋能,更好的维护微博整体的稳定和高可用。

上图我简单列举跨语言rpc的几个技术要点,最下面是可靠性和易用性,比如我们的motan有cluster的概念,每次调用都是从这个cluster里面通过负载均衡(lb)策略来找出可用的节点(endpoint),再通过一种高可用的策略(ha)完成调用。整个过程的各种理念可复用,各种语言照章实现就好。各种策略也是可以按需根据实际情况进行扩展的。而传输、i/o、进程线程模型等各个语言都不一样,需要根据语言自身的特性来选择。

协议和序列化,这是解决跨语言比较重要的一点,因为要让各种语言互相理解对方,互相沟通。

上图的故事大家可能都有所耳闻,人类很狂妄地说,我要建一个塔直通到天堂。但是上帝听到以后觉得很不爽,所以让人类说不同的语言。因为语言不通,导致塔建歪了,整个工程就失败了,这是说交流的重要性。要做跨语言服务化,负责数据通信的rpc框架跨语言是重中之重的基础设施。而协议和序列化如何取舍,很关键。

motan的java版,所要解决的是微博平台内部java服务之间的调用,因此motan1协议时,其实并没有考虑到跨语言的问题,用的是对java比较友好的hessian。后期在跨语言方面,motan1的协议显得对跨语言不是很友好,motan2的协议就给了一个足够容易理解的协议,是一个简单的tcp描述。比如,要请求一些方法、服务的分组,或body,请求发过去,对方语言收到后,怎么样让这个语言理解呢?关键一点就在于序列化。

现在motan使用简单序列化的方式(simple),只支持几种简单的类型。不过我们一直都在迭代中,也正在以前支持更复杂的类型上做扩充。但本质上,它仍是简单的协议,我们会尽量保证它的简单。只有足够的简单,才能在跨语言上降低成本,开发成本还好,主要是语言沟通起来解析的成本,比如,编译型语言往往有各种明确的强类型,而解释性语言往往并不需要强制类型约束,如何让他们很好的沟通,这就需要做取舍。

这里给出一个motanrpc框架简图,微博有一个自研的注册中心叫vintage,也支持其它像zk等一些注册中心。图中给出了motanrpc关键的一些组件和他们之间的调用关系。如果对motan感兴趣的,欢迎大家到github搜索关注,我们有更详细的文档,大家也可以网上搜索。为什么要说这些?因为weibomesh是基于motan来做的。希望大家对motan有个整体的认识。

现有实现跨语言,支持不同语言的motan,最下面是java版,就是最开始的一版,现在支持golang(motan-go)、openresty-lua(motan-openresty)、php(motan-php)。

基于motan-go的weibo-mesh

下一个话题:基于motango的weibo-mesh。数人云敖小剑老师扛着servicemesh大旗已经冲了一段时间了。刚开始我们也看到这一点,其实大家的想法都一样,因为需求和痛点基本是一样的。所以我们严重认同servicemesh是下一代微服务,当然不同人都有自己的理解,通过初步的实践,如果一个很复杂的架构,大家在做服务化,服务化到一定程度,会由于微服务的力度和规模慢慢增大,出现依赖复杂度的增加带来流量交错,难以管控以及服务治理更加困难等一序列的问题。为了解决这些就需要一个东西很好的管控这些服务和流量,这就是为什么会有mesh。

看一下现在业界公认最牛的meshistio是怎么玩的。这里我希望大家关注两点,一个是istio有一个基于envoy的数据传输层,另外是控制面板,istio通过这个控制面板来完成流量调度,鉴权,服务治理等工作。这是istio现在的做法,那微博怎么做的呢?

首先,我们从2016年开始做跨语言的服务化改造,那时还没有servicemesh,刚开始很简单,只想做跨语言,想解决跨语言调用链路过长的问题。比如,原来所有的跨语言服务都是通过restful接口提供的,每个请求都需要层层转发,尤其现在是混合云的架构。举个极端又及其常见的例子,a机房的服务依赖一个restful接口,请求发出后,内部dns解析说这个域名在b机房提供服务,你到b机房后,ha告诉你,这个请求应该去c机房的一个member上去完成,绕了一大圈,很有可能发现那个c机房在公有云上,链路特别长。所以,希望通过实现跨语言的motanrpc,client对server发起直连,通过点对点的通信来解决链路过长的问题。而后来的演进,后面再细说。

今天讲的这个可能比较简单,目的是希望大家能更好的入戏,更好的理解。可以用直连的方式,或用任何其它方式,像我们用了motan。

微博从2016年开始做这个事情踩了很多坑。微博做跨语言,刚才在后面跟老师们交流大家还说grpc把跨语言带跑偏了,当时做跨语言的时候就是想grpc已经做的很好,就想通过motan支持grpc协议的方式来实现motan的跨语言调用。大家可以去翻motan的开源,里面是有grpc支持的,现在也还有(不过最早支持的是php,对接的是鸟哥写的那个yar的rpc框架,最早motan还支持yar协议。)。

所以当时想法很简单,想依托grpc来解决跨语言的问题,所以是上面这样的一个结构,用grpc来搞。

这个结构出来以后,我们开始拿线上的业务来摸索改造。发觉需要把原来的restful接口完全重写成grpc的服务,这样的改造避免不了大范围的代码重写。硬着头皮改了一些后,发觉性能也没有像宣称的那么好。

大家平时可能都会上微博,我们一条微博数据里面可能有百十个字段,所以基于grpc改造就需要把这些字段和服务都用pb进行定义,完了以后发现最终的proto文件写下来一大坨。虽然说请求过程并不需要传递这些proto数据,但是请求数据回来以后需要解析组装。跨语言的时候,比如说php来解析那个对象,性能就比较差了,所以这种方案应对微博这种场景最终被淘汰了,而且就算勉强接受性能的损失,这里也只是基于grpc解决了跨语言的部分。那另外一个很重要的部分---服务治理呢?

因为我们多年服务化演进,motan整个体系在服务治理方面做了很多工作,有很多积累。但是基于motan支持grpc协议来完成跨语言的服务化的服务治理怎么做呢?比如说这个时候php怎么去做服务发现?php每一个过来,都是一次处理整个过程就结束了,没有一个常驻的可用存储每次请求状态的地方来实现服务发现、服务治理这类似的事情。

但是我们也做了很多尝试,比如通过一个本机的后台进程来做服务发现,或者在nginx上基于openresty的timer来实现服务发现,并发结果写到php的$_server变量中,让php能直接使用(上面右图那样)等,但是实践的效果并不理想。

所以整体通过grpc来做跨语言这个方案做下来,效果不是很理想,这并不适合我们的场景。所以我们就走到weibomesh的原形(如下图),我们想既然解释性语言的跨语言服务化由于语言本身的特性和我们的特殊场景(主要是改造成本、性能等方面的考量)下,并不适合直接通过语言本身来实现,那这样的话,还不如去做一个代理。这个代理可能就是weibomesh的一个雏形,类似现在servicemesh中的sidecar。

比如说java启动一个服务,通常情况下,比如javaclient访问这个服务是这样的一个流程:javaclient启动的时候会根据配置去注册中心订阅自己需要访问的服务,请求过程中会有一个线程不断的从注册中心去发现当前可用的节点列表回来,组成client端的一个cluster而完成的调用(如前面motan请求处理简图所示的那样),而如果是php需要通过motanrpc访问这个服务呢?php没法做服务发现,它就把它的请求扔给goagent,服务订阅、发现、治理这些事情让motangoagent来做,其他的都一样。我们做这个的时候,还没有servicemesh这个东西呢,所以一路踩了很多坑。

继续往下想,如果反过来php要做server的时候呢?之前说rpc跨语言所要解决的一堆技术问题,比较重要的是传输模型,怎么选一个比较靠谱的模型来作为server处理请求呢?现在也有很多很好的现成的解决方案,但是我们没有从中选哪一套来做。主要因为微博业务实在是很庞杂,里面有很多php的业务,如果把原来基于lnmp的服务架构改成php直接做server的话,涉及到排山倒海的业务重写,php同学可能会要了我们的命,他们是不会接受的(我要是他们,我也不会干)。所以我们就想,怎么能更简单的把这个事情做了,因为已经有了前面php通过motangoagent去请求rpc服务,我们就继续在上面扩展了一下。

php要做server,我们使用motan-go-agent来帮助php做服务的导出,这个就不是简单的请求调通了,因为motan-go-agent除了负责请求的连通,还负责server端php服务的注册。motan-go-agent的角色更像是是一个motanserver。启动的时候他会把需要导出的php的服务,比如注册中心完整导出,并保持像注册中心服务状态的上报更新。导出后,假如说一个java的client需要调动这个服务,java-client会订阅这些服务,请求过程中会通过服务发现回来的节点对它发起调用。motan-go-agent接到请求后,它把这个请求转给后端的php处理,它之间可能是基于cgi或者是http协议完成的,回来这个请求就完成了。

服务化以后,最直接的感受,原来服务调用时依赖一些lib库、jar包,或者restful接口,现在变成依赖一些服务。在服务调用时,不再是倒入某个包或者curl某个接口,而是直接去注册中心订阅和发现某个服务,这是一个本质的改变。

这个本质的改变提高了服务依赖和复用的效率,但是原来的很多服务都通过这种方式暴露了,也直接提升了服务治理的复杂度,服务治理成本变得相对较高。

但是这一切都是值得的,因为这一来我们统一了服务治理的方式,高可用等各种保障,通用逻辑的封装等实现起来都更轻松自然。

我们基于motan-go-agent提出对像php这样不方便实现服务化的语言,通过正向、反向代理的方式来实现跨语言服务化,java、golang这样很方便实现服务化的语言,有简单的motan实现,或者也可以直接通过motan-go导出服务,这样就只需要实现一个简单的motan-client来完成服务调用即可。

我们服务化做到这个阶段,发现性能已经符合预期,但是当想扩大规模的时候,发现节点和服务数量少。服务化规模很小的时候,比如平台提供几个服务,供几个业务方来调用,可能人工确认,事先配好就没问题。但服务化规模大了之后,就没法人工确认来解决,我们的做法是通过扩展注册中心的功能来解决。

在我们一筹莫展的时候,发现servicemesh这个概念出现了,而且解决的问题和面临的挑战都一样,都是解决跨语言服务化的问题,也都面临流量、服务管控和治理复杂化的挑战。而且,那么多大牛公司在后面做背书,所以我们就马上转到这个方向上来,希望实现一套更符合微博现状的weibomesh。

要更符合微博现状,首先我们在motanrpc、服务治理上有很多积累,这是已有的,不想抛弃的,这是宝贵的技术资源。另一个目标,要做跨语言。所以,一体化的解决方案更符合微博的结构。

另外,微博是混合云架构,弹性云计算平台已经有了。如果新起一个项目,没有任何历史包袱,任何方案都可以。但假如有历史包袱,就不一样了。

这就是基于微博现状做的weibomesh。这里给大家详细的说一下,一个是mesh,跟istio就很像。但是不一样的地方是说,微博体系里除了一些编译型语言,还有一些解释型语言。只有解释型语言需要走用mesh来导出服务的方式。比如java就不需要,因为有比较好的原生积累。istio宣称不需要业务方改任何一行代码。但是我们这里需要一个简单的薄薄的client层,算是我们对自己业务的一点牺牲,对后面的收益极大。

其它都一样,包括服务注册、发现、调用。另外,决策系统好比istio里的控制面板一样,负责发指令。跟istio不一样的地方是,istio中是通过一些请求的header的数据,通过一些规则来做基于iptables的流量转发,而weibomesh不需要转发,因为服务都是通过发现回来的,要调什么服务,就发现什么服务。调用是明确的,出门之前就知道自己要去哪儿,不需要转发。只是有一点,可能发现回来是一堆节点,需要把控的是怎么让流量更均匀,更好的控制流量,所以有一个自动流量调度和弹性扩容。

弹性扩容本身就有,为了应对峰值流量,应对节日。比如原来为了过年,要提前三个月准备好几千万的服务器。

服务治理是motan体系的积累,服务传输就是通过它来收发请求,流量过来,因为请求都经过mesh层,所以通过时时调用的信息,来时时调配流量,对整体系统进行保障。

这就解决了刚才说的,如果微服务力度大,服务划分很细,services规模大到一定程度,多个服务之间,怎么互联互通。

这是微博自动台调动体系,分为几个关键点。容量评估模型、是自动化压测容量评估系统。弹性调动要解决的问题,一个是调配集群的流量,一个是控制集群的规模。怎么调配?首先要度量,度量系统包括:它什么时候是正常的,健康的,什么时候是冗余的。一个系统的冗余度是怎么度量出来的。

通过上面这个公式来度量集群的冗余度,还评估了集群是否需要扩容,流量是否需要调动。

下面这里有几条水位线的概念,一个是安全线,一个是警戒线,一个是危险线,红色的就是危险线,这个时候就需要扩容了。大家可以理解为水库,一个水库能蓄多少水,什么时候水位上来,扛不住了就会发生洪灾。

通过对整个集群的流量评估,可以知道这个集群是否空闲,是否安全。当集群流量扛不住,是否把这些流量切到其它集群,支撑整个弹性调动和动态容量调动。

weibomesh改造收益

weibomesh改造,通用于任何异构系统。有些操作比如解释型语言,因为某些语言特性,不能实现那个功能,但是用mesh的方式,可以在mesh层实现。比如,php请求一个接口,如果接口响应慢,通常的做法是重试,这样会带来危险,因为原来走的都是集群的转发。但是如果直连,如果一个节点慢了,一直重试可能就试挂了。如果重试三次不可以,就不再往这里调了,把它从可用节点里摘掉,解决宕机的问题。

监控里会经常看到一些长尾的调动,发现系统性很好。但是真正跑起来,因为网络或者机器的因素,可能会导致后面的长尾特别长,特别占资源。如果在mesh上,时间长于某一个阈值的时候,就再给发一个,如果后发的请求响应先回来的话,可以把前面的请求抛掉。

体系化是说,不需要在每一套语言里都实现一套。这跟通用的servicemesh方案一样,大家都是为了通用和统一。

这是微博搜索,接入这个系统之后生产当中的一些数据,也经过了一些实验的验证。比如,这是薛之谦和前妻复合事件,这个图同事在很多场合都讲过,原来运营发一个push,大家都提心吊胆。在新浪如果遇到爆炸性的新闻,运营在发push之前,都要通知到所有技术。但是现在不需要这样,比如说那次运营发了一个push,大家可以想象一下山洪爆发,调动系统发现已经过了黄色警戒线了,已经需要扩容了,它就自动扩了,冗余就上来了,这是动态扩容。弹性调动也是如此,用的同一个技术体系。

面向未来架构,重新定义服务化

在开发过程中会遇到很多问题,原来做服务化,所提供的是一些lib、jar包或是像接口之类的,但是现在已经不再依赖那些,现在依赖的是服务,servicemesh里甚至都没有client和server的概念了,都是service。但是在motan里面,是有client和server的。它有两端,因为要去发调动,两端的处理不一样。调动不是通盘都按service来讲的,是通过服务发现,然后对server发起直连。这个过程导致需要每一个实现跨语言的地方,都需要一些特殊的处理。

首先通用性方面,使用motan来刻画一个服务,现在不再依赖包,而依赖于服务。提供的时候,告诉这个服务,注册到那个分组下,调动那个分组下面的服务就可以了,一个很简单的rpc服务调用的方式,上面会有协议,节点和版本号类似的数据,用url来唯一刻画一个服务。

微博有历史包袱,希望已有的系统不要改动太多,以最小的成本得到最大的收益。所以,我们对原来基于jersey开发的cedrusrestful框架进行了适配,在motan框架层面把原来所有的restful接口自动导出为motanrpc服务,通过这种方式减少改造的成本。原来改造的时候,需要把所有restful服务都重写成rpc服务,成本特别高。

支持协议,比如支持grpc,前面有gprc可以去调,现在支持http,http对等的是cedrus(其实就是servlet)。它其实中间传输的是motan协议,但是可以按照http的方式调动一个motan服务,这样的话,client也不需要改任何一行代码。

我们对weibomesh的一些思考,weibomesh跟通常的servicemesh有本质的不同,我们对服务的调用是通过服务发现后直连的,不是通过拦截和转发。通常servicemesh不能直接满足我们的需求,可能大家也有很多类似的场景。所以这些思考希望能给大家一些借鉴的意义。

weibomesh的服务调用需要更轻薄的motanclient,一个原因是降低已有架构的改造成本,一个是泛服务化的概念。比如访问微博,需要调动用户的服务、微博的服务,评论的服务,但是后端的底层资源,比如说mc,kafka等的资源都是服务,如果把这些资源用motan协议来实现资源服务化,只需要一个client就能完成所有的服务调用。而且服务治理体系,流量管控体系都可以复用,解决了大家在开发的时候面对各种资源的不同client实现而各种调研,选型。微博基于openresty实现了motan-openresty版本,就是想以此来对openresty社区在资源服务化方面实现复用,提供更多更方便的服务。

基于泛服务化理念,通过轻薄的client,就可以调所有服务。虽然改造的时候需要改一点代码。泛服务化跟istio的思路不一样之处还在于,泛服务化不止针对自己开发的应用级服务,还有一些底层服务,资源的服务。他们都可以通过封装为简单的motan2协议来进行简单的访问,所有功能都是可编程可扩展的。用过motan的人都知道,motan在做扩展的时候特别简单,比如说要加一个通用的逻辑,无论是扩展一个filter还是新增一种ha策略,这个架构是一个可编程,泛服务化的架构。

weibomesh后期规划

微博是混合云架构,如果是新的公司可能就直接上云了,所以我们后期也考虑在云原生计算方面做一些支持。出于微博本身的技术需要,我们还会在服务治理的整体方案方面做更多的尝试。我们也希望weibomesh能做成更通用化的服务方案,让更多团队受益。

另外,motan还有motan—openresty版本。出于我个人对openresty的理解,我认为openresty可能是目前唯一称得上服务器应用开发平台,把nginx作为一个开发平台,在上面开发各种服务,而且是基于极其简单、表现力极强的lua开发。最令人欣喜若狂的是,重启维护的stream-lua模块让openresty不止能开发http服务,同样能开发tcp、udp服务。基于此的motan-openresty可以复用openresty积累的一切,我希望能在openresty方面做一些事情。

因为就mesh体系来讲,大家可能都是有包袱的。当有既有技术体系的时候,java应用或者其他应用,前面都挂着nginx,从nginx到openresty的迁移成本几乎为零。在整个流量分发当中,它起到比较关键的作用,如果能在这一层上做一些事情,那是不是把很多改造的工作又减轻掉了,平台自己来解决很多问题,应用级别就不需要搞,因为现在比istio,感觉需要强化一点在于说,有一个client,需要大家改一点代码,但是又希望能不能更少的成本,所以我需要做这样的一些事情,这是我自己后期的一个摸索的方向。希望同样对这种思路感兴趣的同学能一起参与,本身整个体系都是开源的,欢迎大家有什么想法或者是有什么问题提出来,一起来做。

因为觉得在这个方面走的也不算特别早,也不晚,现在在业务场景上真正落地的mesh的,微博应该是比较早的,而那个motan-openresty版本可能是基于最新的openrestystream模块开发的。那个现在也作为openresty社区比较标准版的stream模块开发样板项目,也希望大家多多的关注,我今天的分享就到这,谢谢大家。

相关文章
{{ v.title }}
{{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
你可能感兴趣
推荐阅读 更多>
推荐商标

{{ v.name }}

{{ v.cls }}类

立即购买 联系客服