什么是消息传递?

好消息!您已经是消息传递(messaging)方面的专家了(您只是不知道而已)。消息传递已成为我们日常生活的一部分,对于完成我们的日常工作至关重要。作为人类,我们一直在不断地发送和接收信息,我们极其擅长处理数据,来理解它们如何在我们周围的世界中实时运转。但是,我们却将这些尖端技能视为是理所当然的。您的电话响了,您就会接起电话,立即和电话另一端的人分享数据。

世界若没有消息传递会怎样?这个想法似乎有些难以置信,但在计算领域,这是一个非常现实的问题:您如何与软件对话?软件如何与其他软件对话?软件如何与您对话?

这就是消息传递软件的用武之地。作为软件开发者,我们讨厌一而再再而三地解决同样的问题;这不仅是对时间的低效利用,还会不知不觉地带来不一致问题,并有损兼容性。这显然不利于我们向更多的用户推广应用程序。因为信息来回移动是应用程序生态系统的基础,所以开发者只想用一个简单的方式来进行消息传递,这样我们就可以全身心地关注“闪亮耀眼”的新代码的价值和质量。

消息传递之间有何区别?

并非所有的消息传递系统和消息都生来平等。如果想让朋友知道您要晚到几分钟,您可能会发一条短信:“我要晚到 10 分钟”。

单击“发送”后所发生的确切情况取决于消息服务背后的技术。通常,这种类型的系统在可用性和速度方面进行了优化,因为确保消息的传递在计算上成本高昂,特别是在吞吐量较高的情况下。

简而言之,该服务将尽最大努力传输您的信息,最多传输一次,您的朋友可能会收到,也可能收不到。在这个例子中,收不到消息的影响可能相当低,用几句道歉即可轻松化解。

大多数消息传递系统都提供了一系列所谓的“服务质量 (QoS)”选项,支持应用程序设计人员在发送和接收消息方面的各种需求。例如,MQTT 协议定义了以下三项:

  • QoS 0:最多一次
  • QoS 1:至少一次
  • QoS 2:有且仅有一次

对于更重要的消息,您会对“最多一次”的服务质量选项感到满意吗?

举个例子,一个登山团队到达了通信检查点:

[climber]
> Dave has injured his leg.
> Can you arrange for an emergency rescue team?
> It's much warmer than we thought.
> Can you send water to the next checkpoint?

在本例中,处理第二条和第四条消息非常重要。显然,我们在发送这些消息时需要确保传递的服务质量;例如,“至少一次”或“有且仅有一次”(保证消息送到且仅到一次) 的服务质量似乎是可取的。此外,在半山腰,网络并没有完全覆盖,登山者可能没有时间等待回复,所以“已发送”确认必须确实表示已发送。

一段时间后,登山者收到了这样的确认:

[support]
> OK.

这个回复是有意义的,但它回复的是哪条消息呢?

在消息传递用语中,双方之间的这种对话通常被称为“请求-响应”交换。“请求-响应”消息交换模式提供了一种消息关联机制,这样我们就可以知道响应所对应的消息。

回到我们的例子上来:

[climber]
> [messageId=002,correlationId=001] Can you arrange for an emergency rescue team?
> [messageId=004,correlationId=002] Can you send water to the next checkpoint?

[support]
> [messageId=001,correlationId=001] OK.
> [messageId=002,correlationId=002] OK.

现在,我们得到了肯定的确认,这两条消息都已被支持团队所处理,并且正在安排救助人员,物资也在运送途中。

如果您想确保自己的消息被收到,那么一种方法可能就是不断地发送这条消息。对于某些应用程序中某些类型的数据,这种“即发即弃”(fire-and-forget)的消息交换模式非常有效。如果温度传感器频繁发送数据,这未必就是坏事。如果客厅里的温度计每隔 60 秒与供暖系统通信一次,在一次短暂的网络中断导致读数遗漏时,最多只需要 60 秒就会收到下一个读数。

这种方法虽然很简单,但不一定总能得到合适的结果。例如,下面的做法就不太明智:在购物网站上每隔几分钟就点击一次“立即购买”,直到送货司机出现在您家门口时才第一次确认(购买成功)。在这种情况下,我们希望与购物会话相关的消息被交付“一次且仅一次”,或者期望处理应用程序足够聪明,能够去除重复的购买请求。

等待拯救的消息排队

如果没有人倾听,对话便很难称得上完美。语音信箱就是为解决这类问题而发明的。

大多数软件应用程序都是以相互依赖的服务集合形式构造的。其中一些可能是内部拥有的,而其他服务则可能是由第三方所拥有和管理的。回到我们的购物示例上来:Web 前端可能是内部拥有的,而支付服务、履行服务和运输服务则来自外部提供商。这种服务集合依赖于某种形式的异步数据交换,以促进顺畅的通信。如果其中一项服务不可用或运行缓慢,会发生什么情况?我们的业务不能因此而停滞。

消息队列允许应用程序解耦,这样我们就不需要那些长时间运行的脆弱进程。一旦消息被发送,它就会安全地存储在队列中,直到使用应用程序能够处理该消息。发送应用程序可以继续处理其他订单。IBM MQ 和 Java 消息服务 API(Java Message Service API),就是实现此类消息排队的两种方式。

消息传递样式

消息传递系统中使用两种消息传递域(或称为消息传递样式):

  • 点到点消息传递 – 这种消息传递样式使用消息队列,其中消息由单个使用者来处理。消息生产者被称为发送者,消息使用者则被称为接收者。

  • 发布/订阅消息传递 – 在这种消息传递样式中,会将消息副本传递给所有感兴趣的使用应用程序。 消息生产者被称为发布者,消息使用者则被称为订阅者。

Apache Kafka 就是一种基于“发布/订阅”的消息传递系统。IBM MQ 支持上述两种消息传递样式。

保护消息安全

安全是基础。作为消费者,我们会本能地检查网页浏览器地址栏中的挂锁符号,确保自己在网上购物时建立了安全的连接。开发者也需要确保应用程序中组件之间的消息交换是安全的,免于黑客攻击,防止他们伺机查看或操纵这些消息。

在软件中,通过采用行业标准密码和加密技术来实现安全性。软件安全是一个广义的术语,应用程序所需的确切功能会影响消息传递技术的选择。

通常,消息安全主要会考虑以下几个因素:

  • 如何对用户进行身份验证,如何授权用户使用队列和主题等资源?此外,消息传递软件是否与已经存在的身份验证系统相集成?

  • 如何保护传输中的数据?例如,是否支持传输层安全性 (TLS) 以提供加密?

  • 如何保护静态数据?例如,消息或其有效负载在存储时是否加密?

结束语

消息传递软件为在软件应用程序中内置通信功能提供了一种方便而灵活的结构化方式。消息传递软件支持各种典型的 消息交换模式 和一系列 服务质量,让应用程序开发者能够将精力集中在代码的商业价值上。如果消息传递框架运用得当,便可以促进松散耦合的应用程序部署以及解决方案的扩展,同时帮助开发者最大限度地重用现有服务。

本文翻译自:What is Messaging?(2020-05-28)