当前位置: 首页 > 工具软件 > EventMachine > 使用案例 >

Ruby EventMachine - The Speed Demon

漆雕伟志
2023-12-01

By Ilya Grigorik on May 27, 2008

http://www.igvita.com/2008/05/27/ruby-eventmachine-the-speed-demon/


Ruby EventMachine,作为一个框架既有大批拥趸(Evented Mongrel,Analogger, Evented Starling)也饱受非议。一定程度上,人们对框架的恐惧、不安和疑虑(FUD)源于框架的实现语言(Ruby)和底层实现原理(Reactor pattern)的不匹配。Reactor pattern 是一个并发编程模型:接受服务请求,并发地分配到服务处理器。服务处理器根据不同的请求同步分发到相关联的请求处句柄(Request handler)


为什么从Reactorpattern开始?

由于web服务器一般由多进程/多线程实现的意识根深蒂固,当我在几年前加入到滑铁卢大学的一个研究项目之后感到相当吃惊:我们对不同的web服务器架构进行了测试,最出色的集中服务器都是事件驱动(event-driven)的架构。在和所有的人进行了无休止的探讨之后我很快意识到:在每秒有成千上万甚至十万百万个请求的并发环境中,创建进程或者线程切换开销过大。通过比较,一个轻量的经过高度优化事件循环(event-loop)架构在高负载下的表现确实让人眼前一亮。


EventMachine 和 Reactor pattern

在听取了一些不同的关于ruby应用服务器的Presentation之后,我偶然发现这样一种认识:事件驱动的服务器非常适合轻量级的请求,但对于长时间的请求,则性能不佳。从技术上讲是这样的,但是实践来看,未必是真。让我们从一个最简单的例子开始:
require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'
class Handler  < EventMachine::Connection
  include EventMachine::HttpServer
  def process_http_request
    resp =EventMachine::DelegatedHttpResponse.new( self )
    sleep 2 # Simulate a long running request
    resp.status = 200
    resp.content = "Hello World!"
    resp.send_response
  end
end
EventMachine::run {
 EventMachine::start_server("0.0.0.0", 8080, Handler)
 puts "Listening..."
}
# Benchmarking results:
# > ab -c 5 -n 10"http://127.0.0.1:8080/"
# > Concurrency Level:      5
# > Time taken fortests:   20.6246 seconds
# > Complete requests:      10

现在我们使用EventMachine框架构建了一个最简单的HTTP Web服务器。我们通过ab(Apache Bench)测试:并发数设置为5 (-c 5),请求数设置为10(-n 10)。耗时略大于20秒。正如所期望,Reactor同步处理每个请求,相当于并发数设置为1。因此,10个请求,每个请求耗时2秒,一个简单的数学题目。


EventMachine:轻量级并发的Reactor?

同步,是Reactor模式的特性,也是前个例子的瓶颈所在,这也是EventMachine背离纯粹模式的原因。特别地,这提供了一种机制:分配请求到一个线程池中(默认20个线程):
require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'
class Handler  < EventMachine::Connection
 include EventMachine::HttpServer
 def process_http_request
    resp = EventMachine::DelegatedHttpResponse.new(self )
    # Block which fulfills the request
    operation = proc do
    sleep 2 # simulate a long running request
        resp.status = 200
        resp.content = "Hello World!"
    end
    # Callback block to execute once therequest is fulfilled
    callback = proc do |res|
        resp.send_response
    end
    # Let the thread pool (20 Ruby threads)handle request
    EM.defer(operation, callback)
 end
end
EventMachine::run {
 EventMachine::start_server("0.0.0.0", 8081, Handler)
 puts "Listening..."
}
# Benchmarking results:
#
# > ab -c 5 -n 10"http://127.0.0.1:8081/"
# > Concurrency Level:      5
# > Time taken for tests:   4.21405 seconds
# > Complete requests:      10

还是10个请求,并发数设置为5。总共耗时仅仅4秒有余。这意味着新的服务器在并行处理请求。Not quite the Reactor pattern EventMachine advertises, but a very powerful feature nonetheless - nowyou can see where all the FUD is coming from.


Deferrable: 没有线程的并发
Twisted是由Python语言实现的事件驱动的框架。EventMachine从Twisted借鉴了deferrable的思想,包含了Dererrable模块。使用Dererrable模块可以进行并发处理而没有任何线程开销。在Deferrable模式中,你可以指定任意个ruby block(callback或者errback),当deferrable对象的状态改变时,指定的block将会被执行。这是如何起作用的呢?好吧,想象你正在实现一个HTTP服务器,但是为了满足一个客户端的请求,你需要调用另外一个服务器。

require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'
class Handler  < EventMachine::Connection
 include EventMachine::HttpServer
 def process_http_request
    resp = EventMachine::DelegatedHttpResponse.new(self )
    # query our threaded server (maxconcurrency: 20)
    http = EM::Protocols::HttpClient.request(
          :host=>"localhost",
              :port=>8081,
              :request=>"/"
           )
    # once download is complete, send it to client
    http.callback do |r|
        resp.status = 200
        resp.content = r[:content]
        resp.send_response
    end
 end
end
EventMachine::run {
 EventMachine::start_server("0.0.0.0", 8082, Handler)
 puts "Listening..."
}
# Benchmarking results:
#
# > ab -c 20 -n 40"http://127.0.0.1:8082/"
# > Concurrency Level:      20
# > Time taken for tests:   4.41321 seconds
# > Complete requests:      40

没有线程,我们仍在不到5秒的时间内完成了40个请求—— 唯一的限制是我们在前面例子中构建的基于线程的服务器,并发量是20.真是魔法,这就是EventMachine之美:如果你的工作推迟或者阻塞在网络上,Reactor循环将继续处理其他的请求。当Deferred的工作完成之后,产生一个成功的信息并由reactor返回响应。无限潜能,没有线程,没有同步。Dnsruby是最好的例子。 


EventMachine is a speed demon!
 类似资料:

相关阅读

相关文章

相关问答