写点什么

Stomperl:基于 Erlang 的消息中间件

  • 2007-12-20
  • 本文字数:3542 字

    阅读完需:约 12 分钟

(读者可以在 http://stomperl.googlecode.com/ 看到本文介绍的名为“Stomperl”的开源项目。)

我对 Erlang 的兴趣是从去年就已经开始了。但是在断断续续的学习过程中,一个问题一直困扰着我:Erlang 究竟能干什么。就像我在 6 月的一篇 blog 里所写的:

什么时候才需要这样“rocks”的进程管理能力?什么时候才需要如此清晰的进程建模?当然高性能服务器需要。但高性能服务器不是谁都会拿着玩玩的。Ruby on Rails 会走红,因为谁都可以拿它做个网站然后做创业梦。那么 Erlang 呢?它带来的联想是什么?

有必要向对 Erlang 感到陌生的读者做一点背景介绍。作为爱立信在多年前发明的编程语言,Erlang 一直主要被应用在电信领域,并且因为它强大的并行程序设计能力而为人称道。但在很长的时间里,Erlang 一直被视为电信行业的专用工具(它的基础库名叫 OTP,那是 Open Telecom Platform 的缩写,由此可见 Erlang 与电信领域的渊源)。直到前两年,像我这样从事企业应用开发的程序员还是只能通过一篇篇零散而枯燥的论文来了解 Erlang。但随着多核时代的到来,并行计算突然就成了热门话题,Erlang 于是逐渐步入了大众的视野。先是一些函数式编程的爱好者越来越频繁地在各种场合介绍Erlang,然后以“实用主义”著称的Pragmatic Programmers 出版了《 Programming Erlang 》一书。今年这阵风潮也影响到了国内, Erlang 中文社区的成立和两届CN Erlounge 的召开让我们越来越清晰地感觉到这种被昵称为“二郎”的编程语言发出的热度。我之所以投入时间来学习Erlang,主要就是想知道企业软件开发的社区为何对它产生了如此浓厚的兴趣。

尽管买了那本《 Programming Erlang 》,但因为找不到一个适当的项目来练习,学 Erlang 的进展还是不大。在随后的几个月里,我一直努力保持着对 Erlang 社区的关注度。我尝试了 ErlyWeb,也想过在 Ruby 或者 Python 之类的脚本语言里借鉴Erlang 的好处,还在 Erlang 文档计划翻译了两篇论文。直到最近,偶然在一个讨论中听说 Twitter 为提高性能而用到了 Erlang 实现的消息中间件,于是就萌发出动手做一个消息中间件的念头。把几种主要的协议看过一遍之后,我把目标锁定在 Stomp 上,原因就是它足够简单。正如 Stomp 网站上介绍的:

Stomp 是一个非常简单而容易实现的协议……服务器端要做得好可能还不太容易,但要做一个客户端是非常轻松的,你甚至可以用 Telnet 登录到 Stomp broker 并与其交互!

目标确定了,然后就要一步步向前走。我搜索到了 erlstomp 这个项目,但其中的参与者们讨论了好几个月却还没开始写一行代码。不过 Kurt 的一个计划给我指出了方向。和别的通信协议一样,Stomp 无非是规定了客户端和服务器之间对话的语义。(下图描绘了一个经过简化的、典型的 Stomp 通信序列。)先让双方能对话,然后逐渐完善语义,这就是我要做的两件事。

又经过半天的搜索,我发现了一篇题为“采用OTP 原则构造非阻塞的TCP 服务器”的文章和另一篇日语文章,它们都描述了如何搭建一个基本的TCP 服务器架构,并实现了一个最简单的通信协议:echo(客户端发送什么,服务器端就原样发回什么)。抄袭了这个基础,剩下的事情就相对清晰了,无非是一点点把我想要的协议实现出来。同时我还加上了 EUnit 单元测试,用 Gozirra (一个 Java 实现的 Stomp 客户端)创建了验收测试。一个星期业余时间的忙碌之后,这个 broker 已经可以跟验收测试的客户端通畅对话了。于是我给它起了个名字叫“ Stomperl ”(看得出来这是个丝毫不费脑子的名字),发布了0.0.1 版本。可以看到,此时Stomperl 的架构基本上还是第一天抄袭来的那个样子:

+----------------+<br></br> | tcp_server_sup |<br></br> +--------+-------+<br></br> | (one_for_one)<br></br> +----------------+---------+<br></br> | |<br></br> +-------+------+ +-------+--------+<br></br> | tcp_acceptor | + tcp_client_sup |<br></br> +--------------+ +-------+--------+<br></br> | (simple_one_for_one)<br></br> +-----|---------+<br></br> +-------|--------+|<br></br> +--------+-------+|+<br></br> | tcp_stomp |+<br></br> +--------+-------+<br></br> | (one for each)<br></br> +-------+--------+<br></br> | mailer |<br></br> +-------+--------+实现 Stomp 协议的过程轻松得出人意料。一旦习惯了用 Erlang 的方式来看待手中的多个进程,一些在 Java 或者 Ruby 里会相当棘手的并发编程问题就成了小菜一碟,我能够像操作对象一样轻松地操作多个进程,而且还能够清晰地描述它们(前面的架构图中所有的矩形框都是进程)。而且 OTP 的 supervisor 机制实在是一大惊喜:负责处理的tcp_stomp进程如果发生任何异常而崩溃,只会导致与其对应的一个客户端通信失败,作为 supervisor 的tcp_client_sup进程会帮它处理好一切后事,不会让影响波及整个服务器。让我最费脑筋的反而是另一个看似简单的问题:信息放在什么地方最合理。在一篇 blog 里我把保存信息的方式分为参数传递、进程字典、ETS、DETS 和 Mnesia 五种,并在最后总结道:

应该首先考虑靠前的手段,如果有明确的理由表明一种手段不能满足需要时才可以考虑比较靠后的手段。这很费脑子,有时让人沮丧。但经过深思熟虑的程序好过不假思索的程序,发现自己犯错好过犯错而不自知。

在 Stomperl 里,每个 tcp_stomp 进程(以及与之对应的 mailer 进程)负责处理一个客户端的 socket 连接。在这些进程之间有两项信息需要共享:

  • 订阅信息,所有进程都需要知道哪个客户端订阅了哪个消息主题(或者队列);
  • 消息本身需要从接收端传递到发送端。

一开始我曾经考虑用 DETS 来保存订阅信息,但经过认真考虑之后发现,在这种情况下使用 DETS 会造成较大的开销(每次消息传递都需要读写文件),而目前 Stomperl 并不支持多节点分布式的部署,因此 DETS 不带来额外的好处。于是我转而使用了 ETS:在整个系统的“顶端”(即 tcp_server_sup 进程启动时)创建一个 ETS 表,逐次传递给所有子进程,在这张表里存放所有需要在进程间共享的信息。做这个重构花了一些时间,因为几乎所有的函数都需要加上一个参数来传递 ETS 表。但重构完成以后,“消息队列如何实现”的问题也迎刃而解了:直接用这个 ETS 表来实现消息队列,所有进程都能到其中查找自己需要的消息。

刚开始做 Stomperl 的时候我对消息中间件几乎一无所知,“消息发送失败时该做什么”这个问题曾经让我困扰了两三天:如果一条消息没有人接收,它应该被保存起来吗?如果一个订阅者接收成功而另一个接收失败,还应该重发吗?直到与 Stomp 协议的维护者们进行一次讨论之后,才澄清了我的疑惑:

你究竟是采用“消息队列”模式还是“发布 / 订阅”模式?或者说,消息的目标是队列(queue)还是主题(topic)?如果是队列,那么 broker 只把消息发给第一个合适的订阅者,一旦成功就不会尝试发给第二个订阅者。队列里的消息只发送一次、给一个订阅者。如果是主题,那么 broker 会把消息发布给两个订阅者,如果其中之一失败了,它也不会尝试重发,因为按照定义主题就只是把消息发布给所有订阅者,不管这些订阅者是否真的使用(甚至收到)这些消息。

我在 0.0.1 版本中的实现方式实际上是一个“发布 / 订阅”模式。经过一番研究以后我发现大多数消息中间件至少支持消息队列,其中一些同时也支持“发布 / 订阅”模式,于是我又花了一些时间来实现消息队列。还好,得益于 Erlang 强大的进程建模能力,这一步的难度并不大。

除了可以用来练习 Erlang 编程之外,Stomperl 还有什么真实的用途呢?实际上这个问题主要归结为“Stomp 有什么真实的用途”。Stomp 的主要优势还是简单。由于简单,互联网应用、甚至是某些特殊的桌面应用都可以考虑在其中引入这样一个消息总线,从而给应用的各个部分解耦。譬如说 Selenium 或者 Eft 之类的功能测试工具都可能把 TestRunner 和 TestEngine 两部分拆开,后者把用户的输入(可能是某种 DSL)发送给前者去执行,这时就可以考虑引入一个 Stomp broker 来给两部分牵线搭桥。而且借助 StompConnect 可以把任何 JMS broker 包装成 Stomp broker,这也就使得编写 Stomp 客户端程序的工作不大可能被浪费——因为几乎所有重要的消息中间件都支持 JMS。

那么 Stomperl 呢?正如我在项目主页上写的,既然用了 Erlang,那么高效率、可伸缩性、可靠性和并行程序设计的优雅都是题中应有之义。截至今天,我已经准备好发布 0.0.2 版本,这个版本同时支持消息队列和“发布 / 订阅”两种消息模式,并且完整支持了 Stomp 协议中描述的所有消息格式,而代码规模还不到 1000 行。随后我还会为它做更多的兼容性测试和性能测试,并努力为它找到一个实际应用的场景。如果你恰好有一个项目需要一个简单的消息中间件,不妨考虑一下 Stomperl,或许它也会给你一个惊喜。

2007-12-20 20:504267
用户头像

发布了 21 篇内容, 共 31597 次阅读, 收获喜欢 2 次。

关注

评论

发布
暂无评论
发现更多内容

详细图解Netty Reactor启动全流程

bin的技术小屋

微服务 网络编程 中间件 Java IO netty

手把手教你学Dapr - 8. 绑定

MASA技术团队

C# .net 微软 微服务 dapr

Blazor是春天还是寒风里的挣扎

MASA技术团队

C# .net 微软 微服务 blazor

JavaScript 使用 Markdown 制作 PPT

devpoint

markdown 1月月更 slidev

技术分享| 探索视频感知编码

anyRTC开发者

音视频 WebRTC 视频编码 视频压缩 视频感知编码

使用轻量应用服务器部署Docsify在线文档平台

阿里云弹性计算

阿里云 轻量应用 征文投稿

C#8.0 可空引用类型

MASA技术团队

C# .net 微软 微服务 项目

项目动态|Apache Pulsar 2.7.4 版本发布

Apache Pulsar

开源 架构 云原生 中间件 Apache Pulsar

手把手教你学Dapr - 9. 可观测性

MASA技术团队

C# .net 微软 微服务 dapr

盘点 2021 征文大赛获奖名单公布!

InfoQ写作社区官方

盘点2021 热门活动

手把手教你学Dapr - 6. 发布订阅

MASA技术团队

C# .net 微软 微服务 dapr

MS Office 冷门却实用的技巧及软件安装包分享(文末有福利)

淋雨

Office office365

手把手教你学Dapr - 7. Actors

MASA技术团队

C# .net 微软 微服务 dapr

手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序

MASA技术团队

C# .net 微软 微服务 dapr

SpringBoot:如何优雅地进行参数传递、响应数据封装、异常处理?

CRMEB

如何定位并修复 HttpCore5 中的 HTTP2 流量控制问题

阿里巴巴云原生

阿里云 云原生 性能测试 PTS

🏆【Alibaba中间件技术系列】「RocketMQ技术专题」Broker服务端自动创建topic的原理分析和问题要点指南

洛神灬殇

RocketMQ 消息队列 Alibaba技术 Apache RocketMQ 1月日更

深入解析 Apache Pulsar 系列(一):客户端消息确认

Apache Pulsar

开源 架构 云原生 Apache Pulsar 消息中间件

『内存中的操作系统』内存虚拟化又是什么

soolaugust

操作系统 内存

普通开发者,如何成为Apache项目的commiter ?| 人物专访

云智慧AIOps社区

开源 程序员 算法 运维 时序数据

如何提高你的写作技巧

坚果

1月月更

中国AIOps们,你们究竟是在骗谁?

Geek_f56666

云计算 AIOPS 云服务

详解 HDFS 底层交互原理

五分钟学大数据

hdfs 1月月更

阿里云弹性计算年度关键词:强劲、突破、开放

阿里云弹性计算

阿里云 弹性计算 计算巢 神龙架构 无影

Markdown-it 原理解析

冴羽

前端 markdown vuepress 博客开发 markdown-it

淘宝小部件 Canvas 渲染流程与原理全解析

阿里巴巴终端技术

小程序 淘宝 渲染

【渗透技术】一个渗透测试工具人是怎样操作的

H

网络安全 渗透测试

中国AIOps们,你们究竟是在骗谁?

Geek_2749b8

云计算 AIOPS 云服务

全国社保基金理事会副理事长陈文辉一行莅临青藤调研

青藤云安全

中国AIOps们,你们究竟是在骗谁?

观测观测

AIOPS

从 ClickHouse 到 ByteHouse:实时数据分析场景下的优化实践

字节跳动数据平台

数据库 字节跳动 Clickhouse 实时数据分析 bytehouse

Stomperl:基于Erlang的消息中间件_Erlang_Jeff Xiong_InfoQ精选文章