后记

后记 #

来自外部世界的故事 #

我请本书的一些贡献者告诉我们他们在用 ZeroMQ 做什么。以下是他们的故事。

Rob Gagnon 的故事 #

“我们使用 ZeroMQ 来协助聚合全球电信服务器网络上每分钟发生的数千个事件,以便我们能准确地报告和监控需要我们关注的情况。ZeroMQ 不仅让系统的开发变得更容易,而且比我们最初设计的方案开发速度更快、更健壮、容错性更好。

“我们能够轻松地从网络中添加和移除客户端,而不会丢失任何消息。如果我们需要增强系统的服务器部分,我们也可以停止并重新启动它,而无需先担心停止所有客户端。ZeroMQ 内置的缓冲功能使这一切成为可能。”

Tom van Leeuwen 的故事 #

“我当时正在考虑创建某种服务总线,将各种服务连接在一起。市面上已经有一些实现了 Broker 的产品,但它们没有我需要的功能。偶然间,我发现了 ZeroMQ,它太棒了。它非常轻量级、简洁、简单易懂,因为指南非常完整且读起来很顺畅。我实际上已经实现了 Titanic 模式和 Majordomo broker,并做了一些改进(客户端/工作者认证以及工作者发送一个目录,说明它们提供什么以及如何寻址它们)。

“关于 ZeroMQ 最棒的一点是它是一个库,而不是一个应用程序。你可以随心所欲地塑造它,它只是将排队、重连、TCP 套接字等枯燥的事情放在后台,确保你可以专注于对你来说重要的事情。我已经用 Ruby 实现了各种工作者/客户端和 broker,因为这是我们主要的开发语言,但也用了一些 PHP 客户端从现有的 PHP Web 应用连接到总线。我们将这个服务总线用于云服务,将各种平台设备连接到暴露自动化功能的服务总线上。

“ZeroMQ 非常容易理解,如果你花一天时间阅读指南,你就会对它的工作原理有很好的了解。我是一名网络工程师,不是软件开发者,但我成功地为我们的自动化需求创建了一个非常好的解决方案!ZeroMQ:非常感谢你!”

Michael Jakl 的故事 #

“我们在分布式处理管线中使用 ZeroMQ 分发每天数百万份文档。我们最初使用大型消息队列 Broker,它们各自都有自己的问题。为了简化我们的架构,我们选择了 ZeroMQ 来进行连接。到目前为止,它对我们的架构如何扩展以及改变和移动组件的容易程度产生了巨大的影响。丰富的语言绑定使我们能够选择合适的工具来完成工作,而不会牺牲系统中的互操作性。我们使用的套接字不多(整个应用中少于 10 个),但这正是我们将一个庞大的单体应用拆分成独立小部件所需的全部。”

“总而言之,ZeroMQ 让我也保持理智,并帮助我的客户控制在预算内。”

Vadim Shalts 的故事 #

“我是 ActForex 公司的团队负责人,该公司开发金融市场软件。由于我们领域的特性,我们需要快速处理大量的价格数据。此外,最大限度地降低处理订单和价格的延迟至关重要。仅仅实现高吞吐量是不够的。一切都必须以软实时方式处理,并且每个价格的延迟都要超低且可预测。系统由多个交换消息的组件组成。每个价格都可能经历多个处理阶段,每个阶段都会增加总延迟。因此,组件之间低且可预测的消息传递延迟成为我们架构的关键因素。

“我们研究了各种解决方案,以找到适合我们需求的方案。我们尝试了不同的消息 Broker(RabbitMQ、ActiveMQ Apollo、Kafka),但未能达到低且可预测的延迟。最终,我们选择了 ZeroMQ 与 ZooKeeper 结合使用进行服务发现。ZeroMQ 的复杂协调需要相对较大的努力和充分的理解,这是多线程固有复杂性的结果。我们发现,像 ZooKeeper 这样的外部代理更适合服务发现和协调,而 ZeroMQ 主要用于简单的消息传递。ZeroMQ 完美地融入了我们的架构。它使我们能够以最小的努力实现所需的延迟。它使我们摆脱了消息处理的瓶颈,并使处理时间非常稳定和可预测。

“对于低延迟至关重要的解决方案,我完全推荐 ZeroMQ。”

本书如何诞生 #

当我开始写一本 ZeroMQ 的书时,ZeroMQ 社区仍在争论分支(forks)和拉取请求(pull requests)的优缺点。今天,无论如何,这场争论似乎已经尘埃落定:我们在 2012 年初为libzmq采用的“宽松”政策打破了我们对单一主要作者的依赖,并向数十位新贡献者开放。更深远的影响是,它让我们转向了一种与旧有的强制推进模式截然不同的温和有机进化模式。

我之所以对这种方式有信心,是因为我们对指南的工作已经有一年或更长时间,指明了方向。确实,文本是我自己的作品,这或许是应该的。写作不同于编程。当我们写作时,我们讲述一个故事,人们不希望听到不同的声音讲述同一个故事;那样感觉很奇怪。

对我来说,这本书真正的长期价值在于它的示例代码库:用 24 种不同语言编写的约 65,000 行代码。这部分是为了让更多人能够接触 ZeroMQ。人们想要告诉别人如何学习 ZeroMQ 时,已经会参考 Python 和 PHP 的示例代码库——这是其中最完整的两个。但它也是关于学习编程语言的。

这是一个用 Tcl 编写的代码循环

while {1} {
    # Process all parts of the message
    zmq message message
    frontend recv_msg message
    set more [frontend getsockopt RCVMORE]
    backend send_msg message [expr {$more?"SNDMORE":""}]
    message close
    if {!$more} {
        break ; # Last message part
    }
}

这是用 Lua 编写的同一个循环

while true do
    --  Process all parts of the message
    local msg = frontend:recv()
    if (frontend:getopt(zmq.RCVMORE) == 1) then
        backend:send(msg, zmq.SNDMORE)
    else
        backend:send(msg, 0)
        break;      --  Last message part
    end
end

而这个特定的例子(rrbroker)存在于 C#, C++, CL, Clojure, Erlang, F#, Go, Haskell, Haxe, Java, Julia, Lua, Node.js, Perl, PHP, Python, Ruby, Scala, Tcl,当然还有 C。这个代码库全部在 MIT/X11 许可下作为开源提供,可以作为其他书籍或项目的基础。

但这个翻译集合最深刻地说明了这一点:你选择的语言只是一个细节,甚至是分散注意力的东西。ZeroMQ 的力量在于它提供的和让你构建的模式,这些模式超越了语言的兴衰更迭。我作为软件和社会架构师的目标是构建能够世代相传的结构。仅仅以几十年为目标似乎没有意义。

消除阻力 #

我将从我们消除的阻力角度来解释我们使用的技术工具链。在这本书中,我们正在讲述一个故事,目标是以最便宜、最顺利的方式触达尽可能多的人。

核心思想是将文本和示例托管在 GitHub 上,并让任何人都可以轻松贡献。然而,事实证明它比想象中更复杂。

让我们从分工开始。我是一个好的写作者,可以快速地写出大量不错的文本。但对我来说不可能的是提供其他语言的示例。由于 ZeroMQ 的核心 API 是用 C 编写的,因此用 C 编写原始示例似乎是合乎逻辑的。此外,C 是一种中立的选择;它可能是唯一不会引起强烈情绪的语言。

如何鼓励人们翻译示例?我们尝试了几种方法,最终效果最好的是在文本的每个示例上提供一个“选择你的语言”链接,将人们引导到翻译页面或一个解释如何贡献的页面。通常的工作方式是,人们在他们喜欢的语言中学习 ZeroMQ 的同时,会贡献少量翻译或修复现有的翻译。

与此同时,我注意到有一些人非常坚定地翻译了每一个示例。这主要是绑定作者,他们意识到示例是鼓励人们使用其绑定的绝佳方式。为了奖励他们的努力,我扩展了脚本以生成特定语言版本的书籍。我们不再包含 C 代码,而是包含 Python 或 PHP 代码。Lua 和 Haxe 也获得了自己的专用版本。

一旦我们知道谁在做什么,就知道如何组织工作本身了。很明显,要编写和测试一个示例,你想要处理的是源代码。所以我们在构建书时会导入这些源代码,这就是我们制作特定语言版本的方式。

我喜欢用纯文本格式写作。这样速度很快,并且能很好地与像 git 这样的版本控制系统配合工作。由于我们网站的主要平台是 Wikidot,所以我使用 Wikidot 的非常易读的标记格式来写作。

至少在前几章,画图来解释对等体之间的消息流很重要。手动绘制图表工作量很大,而且当我们想输出不同格式的最终文件时,图片转换就成了一件麻烦事。我开始使用 Ditaa,它可以将文本图表转换为 PNG,后来切换到 asciitosvg,它生成 SVG 文件,效果更好。由于这些图是嵌入在散文中的文本图表,所以处理起来非常方便。

现在你可能已经意识到我们使用的工具链是高度定制的,尽管它使用了大量的外部工具。值得庆幸的是,所有这些工具都可以在 Ubuntu 上获得,并且整个定制工具链都位于 zguide 仓库的 bin 子目录中。

让我们逐步了解编辑和发布流程。下面是我们如何生成在线版本

bin/buildguide

具体如下

  • 原始文本保存在一系列文本文件中(每章一个)。
  • 示例代码位于 examples 子目录中,按语言分类。
  • 我们使用定制的 Perl 脚本 mkwikidot 处理文本,生成一组 Wikidot 可用的文件。
  • 我们为每种拥有自己版本的语言都这样做。
  • 我们提取图形,并对每个图形调用 asciitosvg 和 rasterize 生成图像文件,然后将其存储在 images 子目录中。
  • 我们提取内联代码列表(不翻译),并将其存储在 listings 子目录中。
  • 我们对每个示例和代码列表使用 pygmentize 生成 Wikidot 格式的标记页面。
  • 我们使用 Wikidot API 将所有更改的文件上传到在线 wiki。

从头开始做这件事需要一些时间。所以我们存储每个图像、列表、示例和文本文件的 SHA1 签名,并且只处理和上传更改,这使得当人们做出新的贡献时,发布新版本的文本变得容易。

要生成 PDF 和 Epub 格式,我们执行以下操作

bin/buildpdfs

具体如下

  • 我们使用定制的 mkdocbook Perl 程序处理输入文件,生成 DocBook 输出。
  • 我们将 DocBook 格式通过 docbook2ps 和 ps2pdf 转换为每种语言的干净 PDF 文件。
  • 我们将 DocBook 格式通过 db2epub 转换为每种语言的 Epub 电子书。
  • 我们使用 Wikidot API 将 PDF 文件上传到公共 wiki。

在创建社区项目时,降低“更改延迟”非常重要,这是指人们看到自己的工作上线,或至少看到你接受了他们的拉取请求所需的时间。如果这个时间超过一两天,你往往会失去贡献者的兴趣。

许可 #

我希望人们在自己的作品中重复使用这些文本:在演示文稿、文章,甚至其他书籍中。然而,约定是,如果他们混编我的作品,其他人也可以混编他们的作品。我希望得到署名,并且不反对他人从他们的混编作品中赚钱。因此,本文本采用 cc-by-sa 许可协议。

对于示例代码,我们最初采用了 GPL 许可,但很快就发现这不可行。示例的目的是为人们提供可重复使用的代码片段,以便他们更广泛地使用 ZeroMQ,如果这些代码是 GPL 许可的,那将无法实现。我们改用了 MIT/X11 许可,即使对于那些理论上可以使用 LGPL 的更大、更复杂的示例也是如此。

然而,当我们开始将示例代码转变为独立项目(如 Majordomo)时,我们使用了 LGPL 许可。再说一次,可混编性胜过传播性。许可协议是工具;要有目的地使用它们,而不是出于意识形态。