持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
从一个过程到另一个过程的编码数据流,已讨论:
- REST和RPC(其中一个进程通过网络向另一个进程发送请求并期望尽可能快的响应)
- 数据库(一个进程写入编码数据,另一个进程在将来某时刻再次读取)
最后来介绍一下RPC和数据库之间的异步消息传递系统。它们与RPC类似,因为客户端的请求(通常称为消息)以低延迟传送到另一个进程。它们与数据库类似,不是通过直接的网络连接发送消息,而是通过称为消息代理(也称为消息队列或面向消息的中间件)的中介来临时存储消息。
与直接RPC相比,使用消息代理的优点:
- 若接收方不可用或过载,可充当缓冲区,提高系统可靠性
- 可自动将消息重新发送到崩溃进程,从而防止消息丢失
- 避免发送方需要知道接收方的IP地址和端口(在虚拟机经常启停的云部署中很有用)
- 支持将一条消息发送给多个接收方
- 将发送方和接收方逻辑分离(发送方只是发布消息,不关心谁使用)
相比RPC,消息传递通信一般单向:发送者一般不期望收到对其消息的回复。进程可能发送一个响应,但这通常是在一个单独的通道上完成。通信模式异步:发送者不会等待消息被传递,而只是发送它,然后就忘了它。
消息代理
过去,消息代理(Message Broker)主要由TIBCO,IBM WebSphere和WebMethods等公司的商业软件的秀场。最近像RabbitMQ,ActiveMQ,HornetQ,NATS和Apache Kafka这样的开源实现已流行。
详细的传递语义因实现和配置而异,一般消息代理的使用方式:
- 一个进程将消息发送到指定队列或主题,代理确保将消息传递给那个队列或主题的一或多个消费者或订阅者
- 同一主题上,可以有多个生产者、消费者
主题只提供单向数据流。但:
- Con本身可能会将消息发布到另一个主题(可将它们链接在一起)
- 也可发送到一个回复队列,该队列由原始消息发送者来消费(这样支持类似RPC的请求/响应数据流)
消息代理通常不会强制任何特定数据模型,消息只是包含一些元数据的字节序列,因此可使用任何编码格式。若编码是向后和向前兼容,可最大程度灵活地对发布者和消费者的编码进行独立的修改,并以任意顺序部署
若消费者重新发布消息到另一个主题,则可能需要小心保留未知字段,以防止前面在数据库环境中描述的问题图-7
分布式的Actor框架
Actor模型,用于单个进程中并发的编程模型。逻辑被封装在Actor,而非直接处理线程(及竞争条件、锁和死锁相关问题)。每个Actor代表一个客户端或实体,它可能具有某些本地状态(不与其他任何角色共享),且它通过发送和接收异步消息与其他Actor通信。消息传送不保证:某些错误情况下,消息将丢失。由于每个角色一次只能处理一条消息,无需担心线程,每个Actor可由框架独立调度。
在分布式Actor框架中,该编程模型用于跨多个节点来扩展应用程序。无论发送方和接收方是在同一还是不同节点,都使用相同的消息传递机制。若它们在不同的节点,则该消息被透明地编码成字节序列,通过网络发送,并在另一侧解码。
位置透明在Actor模型中比RPC更有效,因为Actor模型已假定消息可能会丢失,即使在单进程中也是。尽管网络延迟可能比同一进程中的延迟更高,但使用Actor模型时,本地和远程通信之间根本上的不匹配发生概率较更小。
分布式的Actor框架实质上是将消息代理和Actor编程模型集成到单个框架。但若对基于Actor的应用程序执行滚动升级,仍需担心向前和向后兼容性问题,因为消息可能会从运行新版本的节点发送到运行旧版本的节点,反之亦然。
分布式Actor框架处理消息编码方案
- 默认情况下,Akka使用Java的内置序列化,不提供前向或后向兼容性。 但可以用类似Prototol Buffers的东西替代,从而获得滚动升级能力
- Orleans 默认使用不支持滚动升级部署的自定义数据编码格式,要部署新版本的应用程序,需建立一个新集群,将流量从旧集群导入新集群,然后关闭旧集群。 像Akka一样,可以使用自定义序列化插件。
- 在Erlang OTP中,对记录模式进行更改很困难(尽管系统具有许多为高可用性设计的功能)。 滚动升级是可能的,但需仔细规划。 一些实验性的新型映射数据类型(2014年在Erlang R17中引入的类似于JSON的结构)可能使得这个数据类型在未来更容易。