当前位置: 代码迷 >> 软件架构设计 >> ZeroMQ指南-第1章-基础-小弟我们为何需要ØMQ
  详细解决方案

ZeroMQ指南-第1章-基础-小弟我们为何需要ØMQ

热度:4909   发布时间:2013-02-26 00:00:00.0
ZeroMQ指南-第1章-基础-我们为何需要ØMQ

我们为何需要?MQ

现在你已经见识了?MQ的实际应用,让我们回到“为什么”。

目前很多应用程序由跨越某种网络的组件组成,不是局域网就是互联网。那么多的程序员最终都在从事某种消息传递。一些开发者使用消息队列产品,但大多是用TCP或UDP来自己开发。这些协议不难使用,但是从A到B发送少量字节和任何可靠方式的消息传递之间是有非常大的区别的。

让我们看看当我们开始用原始TCP来连接时面临的典型问题。任何可重用的消息层都需要解决全部或大部分以下问题:

  • 我们如何处理I/O?你的程序阻塞吗,还是在后台处理I/O?这是设计上的一个关键。阻塞I/O创建的架构不能很好扩展。但是后台I/O要做好是非常困难的。
  • 我们如何处理动态组件,就是会暂时离开的部分?我们是否要在形式上将组件划分为“客户端”和“服务器”,并要求服务器不能消失?那如果我们想要服务器连接服务器怎么办?我们是否要每隔几秒就尝试重新连接?
  • 我们如何表述线上的消息?我们如何将数据组帧才能让它易写易读,缓冲溢出也很安全,对小型消息也很高效,又能够胜任关于戴着狂欢帽的跳舞猫的超大视频?
  • 无法立刻投递的消息我们又如何处理?尤其是正当我们等着一个组件回到在线状态?我们是放弃消息,扔到数据库里,还是放到一个内存队列中?
  • 我们把消息队列存到哪儿去?如果组件从队列读取的速度很慢导致队列堆积是什么原因?这种情况下我们的对策又是什么?
  • 丢失的消息我们如何处理?我们是等待新数据,请求重发,还是建造某种可靠性层来保证消息无法丢失?那如果这个层自身崩溃了又怎么办?
  • 假设我们要使用不同的网络传输会如何。比如说,用多播来替代TCP单播?或者IPv6?我们需要重写程序吗,或者传输已经抽象到某个层了吗?
  • 我们如何路由消息?我们能将同一消息发送到多个对等点吗?
  • 我们如何写一个用于其它语言的API?我们是重新实现一个线路级协议还是重新打包一个库?如果是前者,如何保证效率和稳定堆叠?如果是后者,如何保证互用性?
  • 我们如何表述数据让它能在不同架构间读取?我们要对数据类型强制一种特定编码吗?这是消息系统的工作吗,难道不该是更高层的事吗?
  • 如何处理网络错误?我们是等待重试,悄然忽略,还是中断?

看一个典型的开源项目如HadoopZookeeper,参见src/c/src/zookeeper.c里的C API。当此文写作时,2010年,已有3200行神秘代码,里面有个未公开的客户端服务器网络通信协议。我明白它很有效率因为使用了poll()而不是select()。但实际上,Zookeeper应该使用一个通用的消息层和显式公开的线路级协议。对于团队来说要一遍一遍的建造这个独特的轮子真是个惊人的浪费。

但是如何制作可重用消息层?为何当那么多项目需要这项技术,人们还是在用困难的办法,通过在代码中驱使TCP套接字,并解决着那个长长列表中的难题,一遍一遍?

图7 – 开始时的消息传递Messaging as it Starts

事实证明建造可重用消息传递系统真的很难,这就是为何只有少数FOSS(自由开源软件)尝试过,而商业的消息传递产品为何复杂、昂贵、僵化、脆弱。2006年iMatix公司设计了AMQP(高级消息队列协议),它开始给予FOSS开发者或许是第一个消息传递系统的可重用处方。AMQP比很多其它设计都工作的更好,但还是相对复杂、昂贵、脆弱。需要花数周时间来学习使用,数月时间才能创造出当事情变得复杂时不至于崩溃的稳定架构。

大部分消息传递项目,例如AMQP,在尝试以可重用方式解决这个长列表中的难题时,是通过发明一个新概念,“中介”,来做寻址、路由、和队列。这导致了一个客户端服务器协议或者一组API构建在一些未公开协议之上,来让程序与中介交谈。中介在减少大型网络复杂度方面是非常出色的。但是将基于中介的消息传递添加到产品例如Zookeeper将使它更糟,而不是更好。这将意味着添加一个额外的大型机,和一个新的单一故障点。中介迅速的成为一个瓶颈和一个新的管理风险。如果软件支持,我们能添加第二、第三、第四个中介,还能做一些容错方案。人们这么做着。创建了更多的移动部件、更多复杂度、更多故障。

并且以中介为中心的模式需要它自己的操作团队。你真的需要日夜观察着中介,当它行为不当时用棍子抽打。你需要机子,还需要备份的机子,还有管理这些机子的人。只有在做有很多移动部件、多个团队人员建造的、跨越多年的大型程序时才值得这么做。

所以中小型程序开发者被困住了。要么他们避免网络编程,去做无需缩放的整体应用。要么他们跳入网络编程去做脆弱、难以维护的复杂程序。要么他们下赌注在一个消息传递产品上,最终可扩展性程序基于昂贵、易碎的技术。真没有什么好的选择,这也许是为何消息传递在上世纪死死卡住并激起强烈情绪。对于用户来说是负面的,对于依靠支持和授权来盈利的人则是兴奋而愉悦的。

图 8 – 消息传递变成了Messaging as it Becomes

我们需要的是能做消息传递但方式上如此简单而廉价,它能够以接近零的成本,工作在任何程序中。它应该是一个只需要链接的函数库,无需任何其它依赖。没有附加的移动部件,所以也没有附加的风险。应当能运行在任何操作系统和任何编程语言。

而这就是?MQ:一个高效的、可嵌入库,解决了让一个程序变得富有弹性的跨过网络的大部分难题,成本不高。

特别是:

  • 它在后台线程异步的处理I/O。这些后台线程使用无锁数据结构与程序线程交流,所以并发?MQ程序不需要锁、信号量、或其它等待状态。
  • 组件可以动态的来来去去,而?MQ会自动重连。这意味着你可以按任意顺序启动组件。你可以创建“面向服务架构”(SOAs),服务可以随时加入和离开网络。
  • 当需要时它自动将消息排入队列。以智能的方式,消息排入队列前推送消息到尽可能靠近接收者。
  • 它有几种办法处理满溢队列(称为“高水位线”)。当队列填满时,?MQ自动阻塞发送者,或丢弃消息,取决于你用的消息传递方式(所谓的“模式”)。
  • 它让你的程序用任意传输方式来相互交谈:TCP、多播、进程内、进程间。更改传输方式时无需更改代码。
  • 安全处理低速/阻塞的读者,使用的是取决于消息传递模式的不同策略。
  • 它让你路由消息使用各种模式如请求-应答和发布-订阅。这些模式是你创建拓扑、网络结构的方式。
  • 它让你用一个调用就能创建代理来做队列、转发、或捕获消息。代理可以降低网络的互联复杂度。
  • 它使用简单的线上组帧,转发整个消息并精确重现其发送时的样子。如果你写入一个10K的消息,就能接收一个10K的消息。
  • 它不在消息上强加任何格式。消息就是零到千兆大小的二进制大对象。想要描述数据时你可以在其上选择一些其它产品,例如谷歌的协议缓冲(protocol buffers)、外部数据表示法(XDR)、或其它。
  • 它智能的处理网络错误。有时它会重试,有时它告知你一个操作失败了。
  • 它减少你的碳排放。用更低的CPU消耗做更多事意味着你的机子使用了更少的能源,并且可以让你的旧机器使用的更久。阿尔·戈尔会很喜欢?MQ。

事实上?MQ做的比这更多。对于如何开发支持网络的程序方面具有颠覆性效果。表面上它是一个受到套接字启发的API,你通过它做zmq_msg_recv()和zmq_msg_send()。但消息处理很快变成了中心循环,而你的程序马上分解成一组消息处理任务。优雅而自然。并扩展着:每个任务映射到一个节点,节点们通过任意传输方式相互交谈。进程内的两个节点(节点是线程),机子上的两个节点(节点是进程),或网络上的两台机子(节点是机子)——都是一样的,程序代码没有变化。