本文主要是探究学习比较流行的一款消息层是如何设计与实现的
ØMQ是一种消息传递系统,或者乐意的话可以称它为“面向消息的中间件”。它在金融服务,游戏开发,嵌入式系统,学术研究和航空航天等多种环境中被使用。
消息传递系统基本上像应用程序的即时消息一样工作。应用程序决定将事件传送到另一个应用程序(或多个应用程序),它组装要发送的数据,点击“发送”按钮,消息传递系统负责其余的事情。然而,与即时消息传递不同,消息传递系统没有GUI,并且在出现问题时,在端点处没有人能够进行智能干预。 因此,消息系统必须是容错的并且比常见的即时消息传送快得多。
- ØMQ最初被构想用于是一个针对股票交易的极速的消息传递系统,所以重点是极端优化。该项目的第一年用于设计基准方法,并尝试定义一个尽可能高效的架构。
- 后来,大约在第二年的发展时,重点转向了提供一个通用系统,该系统用于构建分布式应用程序和支持任意消息模式,多种传输机制,任意语言绑定等。
- 在第三年,重点主要是提高可用性和扁平化学习曲线。 我们采用了BSD套接字API,试图清除单个消息模式的语义,等等。
Application vs. Library
ØMQ是一个消息库,而不是一个消息服务器。我们花了几年时间研究AMQP协议(一个金融行业尝试标准化企业消息传递的有线协议),为其编写参考实现并参与了好几个大规模的基于消息传递技术的大型项目,并最终意识到意识到使用经典客户端/服务器模型的智能消息传递服务器(代理)和哑消息传递客户端的方法有问题。
我们首要关注的是性能:如果中间有一个服务器,每个消息必须通过网络两次(从发送方到代理,从代理到接收方),这在延迟和吞吐量方面都会有一定代价。 此外,如果所有消息都通过代理传递,在某一时刻,服务器必然成为瓶颈。
次要关注的是大规模部署:当部署跨组织(如:公司等)时,管理整个消息流的中央授权的概念不再适用。由于商业秘密和法律责任,没有公司愿意将控制权交给不同公司的服务器。在实践中的结果是,每个公司有一个消息服务器,用桥接器连接到其他公司的消息传递系统。整个系统因此严重分散,并且为每个涉及的公司维护大量的桥接器不会使情况更好。为了解决这个问题,我们需要一个完全分布式的架构,该架构中每个组件都可能由不同的业务实体控制。考虑到基于服务器的架构中的管理单元是服务器,我们可以通过为每个组件安装单独的服务器来解决上述问题。在这种情况下,我们可以通过使服务器和组件共享相同的进程来进一步优化设计。这样我们最终得到一个消息库。
我们已经能够证明这种架构比标准方法更高效(更低的延迟,更高的吞吐量)和更灵活(很容易构建任意复杂的拓扑,而不是限定为经典的hub-and-spoke模型)。
其中一个出乎意料的结果是,选择库模型改善了产品的可用性。 一次又一次,用户因不必安装和管理独立的消息服务器而感到开心。 事实证明,没有服务器是一个首选项,因为它降低了运营成本(不需要有一个消息服务器管理员),并加快上线时间(无需与客户协商是否运行服务器,以及管理或运营团队的问题) 。
Global State
全局变量不能很好地与库交互。 即使只有一组全局变量,库可能在进程中也会加载多次。 图1显示了一个从两个不同的独立库中使用的ØMQ库的情况。 然后应用程序使用这两个库的示例
图1: ØMQ 库在两个不同的独立库中被使用
当这种情况发生时,ØMQ的两个实例访问相同的变量,导致竞态条件,奇怪的错误和未定义的行为。为了防止这个问题的出现,ØMQ库中没有全局变量。相反,库的用户负责显式地创建全局状态变量。包含全局状态的对象称为context。 虽然从用户的角度来看,context看起来或多或少像一个工作线程池,但从ØMQ的角度来看,它只是一个存储任何我们碰巧需要的全局状态的对象。在上图中,libA有自己的context,libB也有自己的context。没有办法让他们中的一个破坏或颠覆另一个。
这里的教训很明显:不要在库中使用全局状态。如果你这样做,当它恰好在同一个进程中被实例化两次时,库很可能会被中断。
Performance
当ØMQ项目启动时,其主要目标是优化性能。 消息传递系统的性能使用两个度量来表示:吞吐量 - 在给定时间内可以传递多少消息; 延迟 - 消息从一个端点到另一个端点需要多长时间。
我们应该关注哪个指标? 两者之间的关系是什么? 不是很明显吗? 运行测试,将测试的总时间除以传递的消息数,得到的是延迟。 单位时间内的消息数是吞吐量。 换句话说,延迟是吞吐量的逆值。 简单,对吧?
我们花了几个星期详细评估性能指标而不是立即开始编码,从而发现吞吐量和延迟之间的关系远没有那么简单,而且是与直觉相反的。
想象A发送消息到B(参见图2)。 测试的总时间为6秒。 有5个消息已通过。 因此,吞吐量为0.83个消息/秒(5/6),延迟为1.2秒(6/5),对吗?
图二:从A发送消息到B
再看看图二。 每个消息从A到B需要不同的时间:2秒,2.5秒,3秒,3.5秒,4秒。 平均值是3秒,这与我们原来计算的1.2秒相差很大。 这个例子显示了人们对性能指标直观倾向的误解。
现在来看看吞吐量。 测试的总时间为6秒。 然而,对于A而言,它只需要2秒就可以发送完所有的消息。 从A的角度