微博开源的Motan RPC最新进展:新增跨语言及服务治理支持
https://github.com/weibocom/motan
Motan 是一个基于 Java 开发的高性能的轻量级 RPC 框架,Motan 提供了丰富的服务治理功能和优秀的扩展能力,可以方便的基于 Motan 进行二次开发。Motan 框架于 2016 年开源后取得了社区的广泛好评,并且由社区贡献了众多 PR,开源后增加了全注解配置、不同序列化方式、异步调用、OpenTracing、Restful 等众多功能,得到了快速的发展。
微博平台的服务交互方式主要是基于 Motan 的 RPC 调用,从 2013 年 Motan 诞生,到 2015 年基于 Docker 容器化部署的混合云架构,Motan 框架也随着每次微博服务化的升级而不断发展。目前微博服务化体系上运行着上千个服务,每天完成万亿次的 RPC 服务调用和数百亿的 API 调用。微博的服务化能够实现复杂的服务治理功能,并且能够快速、细粒度的动态扩缩容。
但是 Motan 还有一个跨语言服务化的问题没有很好的解决,在多语言场景下使用时,还需要服务提供方同时提供 RPC 服务和 HTTP 服务。虽然 Motan 支持了 YAR 协议可以在一定程度上解决这个问题,但是还有 Golang、C++、Python 等一些语言的服务需要进行跨语言调用。如何在多语言之间提供统一的服务化能力与标准,是目前微博服务化需要解决的主要问题,也是 Motan 框架目前的研发方向。
一、跨语言服务化的挑战
服务化交互有很多种方式,例如 HTTP、RPC、异步消息队列等,这里主要对 HTTP 和 RPC 这两种常用方式的跨语言方案进行介绍:
HTTP RESTful API
HTTP 本身就是语言无关的协议,一般由服务提供方与使用方通过文档来进行服务约定。这也是目前使用非常广泛的跨语言交互方式,HTTP 服务的服务治理能力一般依赖于代理层和服务调用方自己实现,一般代理服务需要单独部署,所有请求流量经由代理服务进行转发。例如使用 nginx 提供基础的负载均衡与流量控制等治理能力就是最简单的跨语言服务治理方案。
HTTP 的跨语言服务化,有些方案选择了为特定语言实现强大服务治理能力,对其他语言提供兼容能力,例如 Spring Cloud 为 Java 服务提供完整的服务治理能力,包括服务发现、路由器、过滤器、配置管理、熔断器等等。但很难为不同语言提供统一的服务治理能力,特别是在 Client 侧的一些服务治理能力,不同语言都需要做相同功能的开发。
还有一些方案使用本机代理的方式,例如 envoy、linkerd 等可以部署到单机的 HTTP 代理服务,在代理服务中集成统一服务发现与治理能力。这种方案能够做到不同语言的 Client 和 Server 都提供相同的服务治理能力,但服务治理的功能很难做到前一种方案那么强大。
RPC
使用 RPC 作为服务调用方式时,跨语言与服务治理也很难同时满足,对于跨语言型的 RPC 协议,例如 gRPC 等,都能够提供不同语言交互的能力,但是相关的服务治理功能还没有那么完善,做到了’交互’,但还无法做到’治理’。使用跨语言型 RPC 框架时一般会选择使用 HTTP(HTTP2)作为通信协议,这样可以直接应用 HTTP 方式的服务治理功能;
对于服务治理型 RPC,由于选择了在 Client 端进行服务发现与治理,如果要实现不同语言统一的服务治理能力,需要在不同语言的一侧都实现相同的功能。实现起来也相当的困难。因此大部分服务治理型 RPC 专注与为特定语言提供完善的治理能力。
看来,无论哪种服务交互方式,跨语言与统一服务治理都很难做到鱼与熊掌兼得
二、Motan 的跨语言服务化方案
Motan 作为服务治理型 RPC 框架,跨语言服务治理是一个非常大的挑战,为了实现不同语言的统一服务治理能力,我们评估了几种方案,如下表所示:
兼容 HTTP 协议。增加 HTTP RESTful 协议,可以直接提供 HTTP 服务。在 dubbox 中采用的就是这种方案,但是这种方案将 Java 语言的 RPC 调用方式与其他语言独立开来,只针对 Java Server 端提供,没有为其他语言提供服务治理能力。与 Motan 支持 YAR 协议一样,更多的是一种兼容方案。由于只需要做 Java 协议的开发,这种方案研发和维护成本很低,不过能够提供的统一服务治理能力就很低了。
为不同语言提供 RPC 模块。这种方案能做到最贴近不同语言的使用习惯,可以为不同语言提供完全一致的服务治理能力,不论是作为 Server 端还是 Client 端。但这种方式也是成本最高的,包括开发不同语言的版本,以及后续的功能升级与版本维护代价都非常高。
开发一个 Agent 模块,实现绝大部分的服务治理能力。Agent 在不同服务的 Server 或 Client 侧运行。然后为不同语言开发一个很轻量的 Client 与 Agent 进行交互,由于 Client 只实现必要的交互功能,降低了不同语言版本的开发量与维护的成本。
最终我们选择的方案是 Client + Agent 方式。
为不同语言提供非常轻量的 Client,只实现与 Agent 的交互和必要的请求序列化能力;提供一个 local 的 Agent 代理,能够提供不同 RPC 协议交互能力,以及统一的服务治理能力与标准,服务治理的绝大部分功能都在语言无关的 Agent 中实现。通过使用 Agent,可以为类似 PHP 这样的解释型语言提供长链接复用能力,提高请求性能。
这种方案为不同语言提供了语言友好的 Client,由于 Client 非常轻量,后续的维护工作会非常简单;由语言无关的 Agent 模块为不同语言提供统一的 RPC 协议、负载均衡、HA策略等服务治理能力支持,Agent 升级和维护都不需要使用方代码做任何变更。特别是在使用Docker容器化部署的场景中,Agent 可以作为组件打包至基础镜像,业务使用时可以很方便做到无感知升级。
Agent 方式与 nginx 等代理服务相比,Agent 只负责本机的请求转发,不需要额外的服务器资源部署代理服务。Agent 与 Server 端进行直连请求,请求链路更短,可以保持长链接减少建连开销(与使用的 RPC 协议是否支持有关)。
我们使用 Golang 作为 Agent 的开发语言,并设计了对不同语言更友好的新 Motan 协议,不同语言 Client 通过统一的协议与 Agent 与进行交互,Agent 则可以支持多种不同协议扩展,可以根据配置将请求转换为不同 RPC 协议进行转发。
如上图所示,PHP Client 通过 Motan 协议请求 Agent,Agent 可以用 gRPC 和 Motan 协议请求不同的 Server。
Agent 代理在设计上即可以做正向代理也可以做反向代理,这样不同语言就可以提供相同的 RPC 协议服务了。如上图中,gRPC 协议不支持 PHP 做 Server,通过 Agent 进行代理,可以弥补这个缺陷,PHP 也可以作为 Server 提供 gRPC 服务。
三、遇到的问题:
1)HTTP 服务迁移
Google 的 Protocol Buffers(pb)序列化能够提供优秀的跨语言序列化能力,因此我们在做服务化时倾向于使用 pb 作为序列化方式,同时支持原生 gRPC 协议的调用。但是在进行测试时发现已有的旧 HTTP 服务改造为 gRPC 服务还存在一些问题,例如 PHP 在做复杂 pb 对象(50+ 嵌套 feilds,100k 大小)序列化时性能比较差,耗时差不多是相同对象 json 序列化的 3 倍左右;一些业务从 HTTP 调用改造为 gRPC 调用时 Server 端和 Client 端代码修改量巨大,并且可能因为 pb 默认值等原因在业务逻辑中存在一些潜在风险等等。
对于新服务,我们倾向使用 pb 序列化,因为这样可以为服务提供明确的契约;对于 HTTP 服务迁移,我们准备增加一种简单序列化方式,参数和返回值仅支持 utf8 string 以及 key、value 为 string 类型的 map,这种序列化方式简单高效,与原有的 HTTP 请求比较类似,可以很方便的完成旧服务的改造。
2)Agent 监控与运维
Agent 是跨语言服务化的核心,因此,Agent 的运维与保活监控非常的重要,那么 Agent 该由统一的团队进行监控运维,还是有不同业务方各自管理?由于不同的业务由不同的运维团队管理,而 Agent 的波动一般也需要相应的业务人员关注,目前我们倾向的方案为由各业务方进行 Agent 的保活监控。为此我们提供了 HTTP 管理端口来支持探活与工作状态查询,同时也提供了 pid 文件机制,方便使用不同方式方式对 Agent 进行探活。而后续 Agent 也将支持心跳上报等能力,可以支持统一监控方式。
3)Agent 降级
万一 Agent 挂掉了怎么办呢?Agent 是 local 部署,个别 Agent 挂掉,不会造成全局性问题。除了报警、监控脚本自动重启以及运维人工介入外,在 Agent 挂掉时,Client 必须拥有降级能力,能够直接进行 RPC 调用。
如下图所示,Agent 会在本地指定位置维护一份最近一次可用 Server 节点的快照,当 Client 感知到 Agent 挂掉后,可以根据可用 Server 节点的快照按简单负载均衡策略进行直连 Server,保证服务仍然可以正常使用。降级机制可以为 Agent 挂掉时提供一段时间的恢复窗口期。目前 PHP Client 提供了对 Motan、gRPC 协议的 Server 进行直连的能力。
作为服务治理型 RPC 框架,为不同语言提供统一的服务治理能力是 Motan 框架的目标,Motan Agent 基础功能目前已基本完成,正在部分业务中进行调试,等功能稳定之后将会进行开源,非常欢迎对 Motan、对跨语言服务化感兴趣的同学一起加入,共同完善 Motan 框架。