50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

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:504696
用户头像

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

关注

评论

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

对标世界一流!望繁信科技受邀参加2023企业财务数智化转型论坛

望繁信科技

数字化转型 流程挖掘 财务管理 流程资产 流程智能

技术前沿:“环抱”晶体管与“三明治”布线

E科讯

文献解读-The trans-omics landscape of COVID-19

INSVAST

基因数据分析 生信服务

简化登录流程,助力应用建立用户体系

HarmonyOS SDK

HarmonyOS NEXT

国际网络互联,帮助跨国企业全球协同办公

Ogcloud

SD-WAN 企业组网 SD-WAN组网 跨国组网 跨国网络

链路追踪可视化利器之火焰图

观测云

链路追踪

Apache Doris Flink Connector 24.0.0 版本正式发布

SelectDB

实时数仓 Doris 数据导入 Flink CDC 数据同步工具 CDC

《黑神话:悟空》——高科技点亮西游神话璀璨之路

GPU算力

马斯克 节点搭建 AIPC 黑神话悟空 PC集群

mac电脑植物大战僵尸版下载

你的猪会飞吗

mac游戏 Mac游戏下载 mac单机游戏

剖析 Redis List 消息队列的三种消费线程模型

不在线第一只蜗牛

数据库 redis

C++:使自定义类支持迭代器

不在线第一只蜗牛

c++

为什么说Scrum是有效的?(Why does Scrum work)

ShineScrum

公开课 | 金九银十,测试开发面试秘籍大公开!

测试人

软件测试

软件测试学习笔记丨Docker 安装、管理、搭建服务

测试人

软件测试

数据资产入表全流程解析,助力企业数据要素价值释放

袋鼠云数栈

移动医疗|基于音视频SDK和即时通讯IM技术实现线上问诊技术

ZEGO即构

直播技术 音视频sdk 互联网医疗 在线问诊 直播SDK

告别性能瓶颈!四大关系型数据库向 TDengine 迁移的最佳解决方案

TDengine

数据库 tdengine

SelectDB 多计算集群核心设计要点揭秘与场景应用

SelectDB

数据库 数据仓库 云原生 存算分离架构 云数据仓库

阿里云AMD护航,天谋科技携IoTDB登顶TPCx-IoT国际权威榜单

Apache IoTDB

前端 Vuer,为自己的项目搭建一个 Playwright 测试环境吧,给自己多一点安全感!

OpenTiny社区

Vue 前端 OpenTiny

京东技术专家的修炼之道——成为一名“六边形战士”

京东科技开发者

AutoBots在ToB订单履约场景的落地应用

京东科技开发者

Rust 助力无服务器构筑云计算新引擎

亚马逊云科技 (Amazon Web Services)

云计算 Serverless rust

爽了!直接口诉让豆包MarsCode 开发文章评论区BOT!

TRAE.ai

人工智能 编程 AI 开发

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