EventMachine是Ruby社区的Reactor模式的实现。
所谓Reactor
模式,通过运行一个事件循环,将输入分发给对应的处理器,处理过程全权交给处理器,从而实现同时处理多个输入,是实现高并发的利器。几乎每个语言都有对应的实现,比如Pythong的Twisted,最近很火的Node.js。
这次我们通过实现一个简单的HTTP File server来探索EventMachine。
通过Rubygems可以安装它:
1
|
|
Beginning Sample
我们先从一个简单的例子入手,以下代码实现了这样的一个服务器,打印发过来数据,并返回Yike
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
来解释几个EventMachine的API:
- EM.run 这个方法初始化并启动一个事件循环。
- EM.stop_event_loop 这个方法顾名思义就是停止事件循环。在这段代码中我们注册了两个Signal,INT和TERM,用来在命令行用Ctrl-C停止程序。
- EM.start_server 启动一个TCP服务器并监听传入参数的host和port,最后一个传入的参数是具体的行为逻辑实现,可以是Module或者是Class。
代码中TcpSample module就是具体的Connection逻辑实现,只要实现几个由EventMachine Connection约定的方法,比如收发数据的receive_data
和send_data
。EventMachine会在运行过程事件被触发时回调Connection里的方法。具体关于EventMachine::Connection的文档请点击这里。
我们可以用telnet来测试它:
1 2 3 4 5 6 |
|
第一个简单的例子就这样演示完成了,继续下一步。
Toy File Server
接着步入正题,实现HTTP File Server。一句话来解释HTTP服务器做的事情,就是解析来自客户的Request,然后依照请求生成Response。这里的演示代码如题目所示,只是个Toy,按照请求返回静态文件。
首先需要接受并解析Request。EventMachine已经附带了好几种Protocol的解析,其中包括实现了HTTP的HeaderAndContentProtocol。注意这里的各种Protocol实现都是继承第一个例子中讲到EventMachine::Connection,并为各自的协议包装了一个receive_xxx
的回调方法,HeaderAndContentProtocol的回调方法名为receive_request
。我们的HTTP Toy要做的就是继承HeaderAndContentProtocol
,在receive_request
方法中实现File Server的逻辑。
1 2 3 4 5 |
|
先来完成HTTP Headers的解析:
1 2 3 4 5 6 7 8 9 |
|
接着实现Callback方法receive_request
,主要的逻辑是查找文件和拼装Response并返回:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
以上代码的close_connection_after_writing
方法也是EventMachine的API之一,文档在这里。这个方法会等待send_data
的完成再把与客户端之间的连接关闭。上述大段代码的作用就是读文件并用send_data
返回。
把它跑起来并通过浏览器可以测试一下它:
查看完整的实现代码请点击这里。
Benchmark
最后来对比异步IO和同步的IO效率相差有多大,和本文实现的简单http file server对比的是Rack。用Rack来做对比是因为它几乎是最小最快的HTTP File server实现。代码如下:
1 2 |
|
压力测试用的是HTTPerf,作为测试Fixture的是一个名为index.html的小文件(47B)。
Rack的File middleware在并发超过350的情况就歇菜了:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
350个并发的请求用了接近2秒的时间,速度是177个连接每秒。接着再来测试我们的HTTPToy:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
完胜,和前面的差距不是一星半点,一秒内响应600个连接。如果继续提高并发数,到了700以上我们的HTTPToy也会出现不稳定的情况(崩溃或连接失败)。
Conclusion
EventMachine应用的场景和Node.js基本一样,IO密集的高并发场景,比如
- Web Socket服务端,https://github.com/igrigorik/em-websocket
- 并发的HTTP Client,https://github.com/igrigorik/em-http-request
- Proxy,https://github.com/igrigorik/em-proxy
在生产环境中大量使用EventMachine公司就是PostRank,这个公司基于EventMachine开发了大量的框架和库,有兴趣可以点击igrigorik和postrank-labs的Github帐号。
最后谈下EventMachine缺点,和其它的Reactor模式实现一样,对付CPU密集的应用不行,而且使用的库全部都必须是异步,不然会把Main Event Loop阻塞(其后果是处理速度大大降低)。而像Node.js程序里出现了大量Callback的情况,在EventMachine上会好一点。
本文中的运行环境是Mac OSX Lion,Ruby 1.9.3-p0。