Ruby 因为其 1.8 版本的用户空间线程模型而饱受批评。 Luc Castera 在 RubyNation 会议上给出了通过 Ruby 和元组空间进行并行编程的演讲。在介绍了目前诸多应用(如 Ruby 1.8、Ruby 1.9、JRuby 和 Erlang 等)所实现的不同的线程模型之后,Luc 介绍了 Ruby 的多进程模型(图片和引文均来自于该演讲):
- 优点:
- 不再需要共享内存
- 可利用多 CPU 来提升性能
- 在调用阻塞的系统调用之时不会阻塞全部的线程
- 可伸缩性
- 容错性
- 缺点:
- 进程的创建、执行和清理代价都很大
- 需要使用大量内存(将 Ruby 虚拟机载入每个进程之中)
- 需要一种方式来实现进程间通信
实现进程间通信有多种选择:数据库(例如在 Mongrel 或者 Thin 等应用服务器集群中使用)、RabbitMQ 等队列( Nantie 的解决方案)、DRB、ActiveMQ,或者元组空间。
Luc 比较了两个元组空间的实现: Rinda 和他自己开发的 Blackboard。元组空间提供了一个元组的容器,可以实现并行访问。元组一般提供三个访问原语:write(将一个元组写入元组空间),read(从元组空间中读取一个元组),take(从元组空间中读取一个元组并将其移除)。
Linda 是一个协调模型,由 David Gelernter 和 Nicholas Carriero 提出,用于解决全局对象协调的问题。
Rinda 是 Linda 的 Ruby 实现,而且是一个内置程序库。一个 Rinda 元组可能看起来是这个样子的:t1 = [:add, 5, 9]
,客户端可能会将其解释为一个将两个数相加的任务。
Rinda 最大的缺陷是其不支持持久化,这样一旦程序停止或崩溃的话,所有在元组空间中的元组则会丢失。
这便是 Luc 开发 Blackboard 的原动力。Blackboard 是一个基于 Key-Value 数据库 Redis 的一个元组空间实现,这样它便拥有了持久化的能力。
[Redis] 和 memcached 很相似,但是数据集不易失。取值可以为字符串,就如同 memcached 一样,但是还支持使用原子操作来 push/pop 诸如列表和集合之类的元素
该 API 的使用范例如下:
ts = Blackboard::TupleSpace.new ts.write [:calculator, :add, 1, 2] ts.take [:calculator, :add, nil, nil]
基准测试显示了 Rinda 和 Blackboard 的区别(摘自该演讲):
Rinda Blackboard Write (1000) 0.042749 0.253068 Take (500) 0.082744 15.844250 Read (500) 0.020098 20.098478 当前的实现只是第一步,Luc 计划弃用 Redis 而实现一个 Erlang 的 Blackboard 定制实现,这样就可以方便地使用 Ruby 这样的第三方语言进行调用。
查看英文原文: Exploring Tuple Spaces Persistence In Ruby With Blackboard
评论