内容概要
本文主要分析 WeiboMesh 在跨语言方面的探索。
第一篇文章我们梳理了 WeiboMesh 的演进历程。从单体应用到 RPC 服务化、容器化,到后期的混合云应用以及跨语言支持,再到目前的 WeiboMesh,Motan RPC 在其中扮演了至关重要的角色。
本文我将从 Motan 的服务治理和跨语言两个方面对 Motan RPC 做一个简单介绍。帮助大家更好地理解我们在 WeiboMesh 选型和落地过程中的一些取舍。
Motan RPC
Motan 从 2013 年上线至今,经历了无数次流量洪峰的检验,效果有目共睹。作为一个长于服务治理、轻量、具备良好扩展性、易于二次开发的高性能 RPC 框架, Motan 在微博的服务通信、微服务化拆分以及分布式服务治理等诸多场景中被大量使用。
RPC 其实是一门比较有历史的技术,其思想最早可以追溯到 1976 年的“信使报”(Courier),这是一个计算机通信协议。顾名思义(RPC 全称为:Remote Procedure Call,中文译作:远程过程调用),它主要解决远程服务通信的问题。
RPC 协议通常包括 stub、通信协议、RPC 消息解析这几个部分,一般的 RPC 服务框架,除了实现 RPC 协议规定的点对点通信外,还包含服务发现与治理等相关功能。Motan 就是一个比较有代表性的服务治理型 RPC 框架。

Motan 服务治理
Motan 最初基于 Java 开发,当时微博迫切的服务化改造需求促成了 Motan 项目的启动,诞生之初 Motan 就在服务治理、扩展性和高可用方向做足了工作,实现了完备的服务发布、订阅机制;失败重试、快速失败、异常隔离等高可用策略;低并发度优先、一致性 Hash、随机请求、轮询等负载均衡策略以及 SPI 扩展机制、调用统计、流量控制等。

结合微博自研的 Vintage 注册中心(类似开源的 zookeeper 、etcd 等),服务端启动时会向注册中心按照服务(Service)和分组(Group)注册自己的服务(当然还有版本号、协议等其他信息),并不断向注册中心汇报自己的健康状态,保证注册中心有最新的集群状态信息。
客户端启动时根据服务名称及分组等信息订阅所依赖的服务, 同时将服务发现线程实时发现回来的最新可用节点组成 Cluster,并使用所配置的负载均衡策略获取最终访问的服务节点,然后使用同样配置化的高可用策略对服务端发起点对点的请求。当服务端状态发生变化时,注册中心能够实时同步对应变化并通知客户端更新相关的服务信息。
Motan 在服务治理方面通过对服务分组,解决了服务环境隔离、微服务切分、就近访问等问题,引入 Cluster 对服务发现的结果进行抽象,屏蔽了底层对节点的操作,并使用配置化可插拔的负载均衡及高可用策略,完成了对服务端的均衡、高可用调用,注册中心实时维持整个集群的最新状态,提供了自动化的可用性保障,基于注册中心实现的流量控制与指令系统对集群状态的干预提供了支持,也为后来 WeiboMesh 的动态流量调度建立了可编程的操作机制。

扩展性方面,最初的 Java 版 Motan 基于 Java 的 SPI 机制对相关功能做了良好的解耦和模块化拆分,支持对过滤器、高可用策略、负载均衡策略、序列化、传输协议、注册中心、通信机制等进行扩展或二次开发。
Java 版 Motan 作为 WeiboMesh 的先驱,在高压的生产环境下经历了多年的检验和打磨,沉淀了丰富的服务治理和高可用相关经验,形成了事实的微博服务化规范,也为后面的跨语言服务化和 WeiboMesh 奠定了坚实的基础,尤其是跨语言服务化,只需要语言各自使用最适合自己的姿势来实现这些规范即可,服务治理等相关理论和经验完全得到了复用和升华。
Motan 跨语言
跨语言服务化是 WeiboMesh 探索实践过程中关键的一环,虽然前面 Motan RPC 沉淀的服务治理等相关理论、经验、规范为我们的服务化能力加持不少,但理论、经验、规范都是语言无关的,而跨语言服务化更核心的问题在于与语言特性息息相关的跨语言服务调用和治理。
你可能会问,这里不是在讨论 WeiboMesh 吗?为什么一直扯着跨语言不放?因为在微博场景下,异构系统间服务的通信都是基于语言中立的 HTTP 协议实现 RESTful 接口来完成的,而除此之外微博平台内部还有大量的 RPC 服务也希望能直接暴露给业务方,而不用在前面又包装一层 RESTful 接口。
对于有历史沉淀的团队来说,这些沉淀不一定是包袱,架构取舍的过程中,可能有的部分真是无法放弃或至少是无法立即放弃的,我相信这样的情况不止在微博的场景下会遇到。
Service Mesh 刻画的是一个语言中立的通信和控制层,介于应用层和传输层之间,而 WeiboMesh 始于跨语言服务化,解决了跨语言服务化问题的同时也具备传统 Service Mesh 的服务治理能力,当然也兼顾了微博已有的技术积累。
跨语言服务调用的关键在于解决跨语言通信的问题以及解决异构语言的沟通问题,即如何让语言间互相了解对方。
这里包括通信协议和序列化协议两个方面。
通信协议(比如 gRPC、thrift)解决的是服务调用的问题,以哪种协议封包传输,关注协议的结构及传输、解包效率。
序列化协议则关注相关协议是否为一种语言无关的协议(比如 gRPC 使用的 PB 序列化协议),或者如果是跨语言协议的话,是否对跨语言足够友好,支持足够多的语言数据类型,更多的语言原生的数据结构。
为了以尽可能低的成本达到跨语言服务化改造的目的,我们选择了将第一代通信协议 Motan-1 升级为对跨语言更友好的 Motan-2 协议,由于 Motan 最初基于 Java 开发,默认使用对 Java 对象结构支持更好的 Hessian 序列化协议,当时为了达到更高的传输解析性能, Motan-1 协议与序列化协议有些耦合的地方,现在为了对跨语言支持更友好,在 Motan-2 中我们将其与序列化协议做了彻底的解耦,并且将序列化协议升级为语言中立的 Simple 序列化协议。
Motan-2 使用经典的 header-payload 消息模型来描述请求,而 Simple 序列化通过对简单的数据类型以及多参数支持来满足请求 body 的序列化需求。

跨语言 RPC 的另一个核心在于跨语言服务的治理,以往异构系统的服务依赖 RESTful 接口,服务发现和治理由部署在集群前端的四、七层组件来保障,到达集群的请求并不知道自己应该由哪个结点处理。就像你去医院看病挂号,挂完号后并不知道要去哪个科室找医生,而是把号交给分诊台,分诊台的护士帮你分配医生,告诉你去哪个科室。
这种层层转发的请求处理形式,属于 Server 端的服务治理范畴。而基于 RPC 的系统则不然,Client 依赖哪些服务事先是明确的,启动时已经订阅了这些服务,运行过程中会有服务发现进程(或线程)实时从注册中心将所依赖服务的最新集群信息同步过来。
这就像你去某个地方旅游,怎么去,出门前已经查好,买好了票到时候直接去就可以。直接发起点对点通信,是 Client 端的服务治理范畴,Motan Java 版 Client 实现了完备的服务治理功能,但是不是所有语言实现起来都如 Java 这样方便。
抛开语言特性带来的复杂度不说,每种语言都需要实现一遍业务无关的服务治理逻辑,显然太粗犷,太浪费。
这就是为什么需要把这些业务无关而所有业务又都需要的通用逻辑单独提出来的根本原因,而 Service Mesh 更巧妙的一点在于不但把这部分逻辑从业务代码中移出去,而且是在原有的传输层和应用层之间抽象了一层,将这部分功能以基础设施的角色独立提供出来,并且对业务透明化,这也是 Service Mesh 理念的精华所在。
我们在微博跨语言服务化改造过程中深谙这种理念,所以 Motan 在服务治理和跨语言方面的积累最终得以演化成了 WeiboMesh。至此,大家应该对 WeiboMesh 落地过程中的一些架构取舍有了更多的认识。
下一篇文章中,我将对 Service Mesh 的实现规范以及 WeiboMesh 核心的 Mesh 层组件实现做一些简要分析,希望能帮助你基于 WeiboMesh 对 Service Mesh 理念有一个更全面的理解。