微服务间的通信如何选择

微服务间的通信如何选择

Melvin Koh

如果我们想要构建一个生产就绪的系统,那么必须要权衡所有因素,其中选择微服务间的连接方法更是其中的一个难点。

作者在本文中介绍了一些常见的通信方法,并简要概述了其项目背景以及为何最终选择了RPC。

在决定微服务间连接方法前,我们需要搞清楚两个概念:

  1. 架构风格(Architectural Style)
  2. 传输协议(Transport Protocol)

架构风格

在使用服务时如何形成有效负载?是有状态还是无状态?我们应该采用REST、SOAP、JSON、XML,还是其他消息格式?

传输协议

我们应该用哪种传输协议?应该采用HTTP、HTTP2、消息总线、TCP socket,还是UDP?

时下流行的通信方法

一些主流的选项如下:

  1. REST over HTTP(S)
  2. 通过Message Broker进行消息传递
  3. RPC (跨语言或单语言)

REST over HTTP(S)

自Roy Fielding提出RESTful架构自提出以来,一直都是备受欢迎的方案,特别是在Web应用的开发中。Fielding提出的约束虽然不是标准,但在声明我们的API为RESTful之前,应该始终遵循这些约束。

HTTP上有各种各样的REST,因为没有强制执行的标准。开发人员可以自由选择以JSON、XML或某种自定义格式形成请求有效负载。

REST over HTTP(S)仅意味着使用REST架构风格并通过HTTP(S)发送请求。

例如:JSON-RPC

通过Message Broker进行消息传递

该选项基本上通过将微服务连接到集中消息总线来工作,并且服务之间的所有通信都通过backbone发送消息来完成。

例如:Python中的Nameko

RPC (跨语言或单语言)

远程过程调用在分布式系统中并不新鲜,它通过在网络上的另一个设备上执行函数/方法/过程来工作。

按照RPC标准,RPC 5531:

  1. RPC应该与传输协议无关:TCP、UDP、egal!因此,不保证可靠性
  2. 事务ID用于确保最多一次的semactics,并允许客户端应用程序匹配对呼叫的回复
  3. 超时和重新连接需要处理服务器崩溃,即使使用了面向连接的协议(TCP)
  4. 不指定服务和客户机的绑定,具体由实现人员决定
  5. RPC实现的强制性要求:
    • 被调用过程的唯一规范
    • 响应消息与请求消息匹配的规定
    • 对调用方进行服务身份验证的规定,反之亦然

例如:gRPC RPyC

项目背景

我们有一个一体化Web应用(用Django编写),性能尚可接受。有些服务可以作为单独的服务解耦。我正在以渐进的方式将我们的系统架构转变为微服务架构,其中一项重要工作是决定通信方式。

为什么选择RPC

有很多文章主张用REST替换RPC,说RPC是属于“石器时代”的技术,但也有人说RPC简单易用。而我的立场是中立的,要根据实际的项目来做选择。

以下是项目的主要要求:

  1. 没有单点故障 -> 排除消息队列传递
  2. 错误返回给调用者/客户端/消费者
  3. 提供原生体验的服务接口

由于对调用方的错误返回对我们很重要,RPC是一个很好的候选,因为许多RPC框架将服务器函数中出现的任何异常返回给RPC函数调用方。

大多数RPC框架消除了message broker的需要,因此避免了单点故障。

大多数RPC框架允许远程过程调用,如:

import my_remote_function  
try:  
    my_remote_function.validate_user(my_user)
except ValueError as e:  
    logging.error(e.message)

还有比RPC更好的选项吗?

RPC框架的类别

RPC框架大致可分为以下两种:

  1. 单语言PRC框架
  2. 跨语言PRC框架

单语框架,仅支持单一编程语言。在Python中这类类别的一个很好的候选者是RPyC。RPyC具有易于使用的标准RPC功能,并使用TCP作为其传输协议。

使用RPyC(单语框架)的优点是不需要编写单独的服务接口。缺点是对不同Python版本的支持不足,当然,正如其名称所暗示的那样,缺少对跨语言的支持。

跨语言RPC框架支持多种编程语言,但成本很高。gRPC是我使用的框架之一。

gRPC由Google提供支持,涵盖了从C++、Ruby、Python到Dart的各种编程语言。为了支持多种编程语言,必须定义一个通用的服务契约,通常是协议缓冲区(.proto)文件。Service Contract使用服务器提供的参数定义函数,以及要传输的消息格式。对于gRPC,将协议缓冲区文件编译为特定语言的文件(例如Python中的.py文件),这会在您开始拥有多个版本的service contact时产生问题。这使我们很难跟踪不同版本的客户端存根和服务功能。

小结

总结来说,通信方式需要case by case地选择,而这些以上基本上是我在选择微服务之间的通信媒介时所考虑的问题。

关于Rainbond

当下,已经有很大一部分公司完成了单体架构向微服务架构的迁移改造,并在疲于应对大量微服务间通信问题时,开始考虑采用Service Mesh微服务架构作为服务与服务直接通信的透明化管理框架,以插件式的方式实现各种业务所需的高级管理功能。

而开源PaaS Rainbond提供了开箱即用的Service Mesh微服务架构,部署在Rainbond上的应用原生即是Service Mesh微服务架构应用。