Gem EventMachine 使用

towonzhou · 2013年10月22日 · 最后由 kimmg 回复于 2017年10月09日 · 11420 次阅读

eventmachine 使用

eventmachine 是一个轻量级的事件驱动 I/O 模型框架,是 ractor 模型的。
下面一步步来介绍一下 eventmachine.(eventmachine 可以简写成 em)

1. 先从打印 hello world 开始。

$ vim test1.rb

#test1.rb
require 'rubygems'
require 'eventmachine'

EM.run {
  EM.add_periodic_timer(1) {
    puts "hello world"
  }
}

这个程序每隔一秒输出 hello world.
它是如何工作的呢?require 之后,调用了 EM.run, 它接收一个代码块做为参数。
(TODO 具体它是怎么工作的不太清楚,应该是一个事件循环,执行EM.run {puts "i am in run"}会有一个输出后然后阻塞住.....)
add_periodic_timer 是周期定时器,上例表示每隔一秒执行一次代码块参数。这个代码块称之为回调。
可以使用 EM.stop 来结束事件循环

require 'rubygems'
require 'eventmachine'

EM.run {
  count = 1;
  EM.add_periodic_timer(1) {
    EM.stop if count == 5
    puts "#{count}: hello world"
    count += 1
  }
}

以上代码打印 5 次 hello world 后结束。

2. 高效的 IO

1. 简介

高效的 IO 是 eventmachine 的全部意义所在,在你使用 eventmachine 进行网络 IO 编程时,要么直接使用 evnetmachine,
要么使用 eventmachine 扩展的某个 lib,一般以 em-开头,eventmachine 自带了两个不同的 HTTP client,但都有些问题,
这里推荐一个强大的模块:em-http-request
为什么要使用 em 来进行 IO 呢,如果你是使用标准库中的 Net::HTTP,向一个 URI 请求,预计要 10s 的响应时间,那么你的进程就会阻塞 10s,在此期间,上面的定时器都不会触发,一直到返回结束或超时。
$vim test2.rb

#test2.rb
require 'rubygems'
require 'eventmachine'
require 'em-http'

EM.run {
  http = EM::HttpRequest.new("http://www.goole.com").get
  http.callback { |h|
    puts h.response_header
    EM.stop
  }
}

EM::HttpRequest句 get 一个 url,callback 接收一个代码块参数,获得数据后执行。
连接的形式如 EM::HttpRequest.new(url,connect_options).methods request_options
具体可见em-http-request的文档
在设计 API 接口时,需要有办法来区分响应成功或者失败。在 ruby 中,有两种常用的方法,一种是返回 nil,一种是抛出异常. EM 提供了一种更为优雅的方案:the deferrable.
deferable 是一个对象,通过它你可以添加上成功或者失败后的回调方法,分别为 callback 和 errback.有兴趣可以看源码.
前面代码中调用 HttpRequest#get 时,返回的就是一个 deferrable.(实际上返回的是一个 EM::HttpClient 实例对象,它被 mixin 到了 EM::Deferrable 模块中.),当然也有更为通用的方法,就是直接使用 EM::DefaultDeferrable.

2. 示例

当我需要写一个新的 API 时,一般会使用 HTTParty,因为它很容易使用。

慢慢写 不着急

感觉如果用 eventmachine 了,还不如直接用 node.js 算了,社区更活跃,看看eventmachine的发布节奏,着急啊!

EM.run 的核心是一个循环 https://github.com/eventmachine/eventmachine/blob/v1.0.3/lib/em/pure_ruby.rb#L311

loop {
  @current_loop_time = Time.now

  break if @stop_scheduled
  run_timers
  break if @stop_scheduled
  crank_selectables
  break if @stop_scheduled
  run_heartbeats
}

PS: reactor 不是 ractor

#2 楼 @zhangyuan 为什么执行 EM.run {puts "i am in run"}的时候不是一直输出"i am in run"呢?

#3 楼 @towonzhou EM 会先执行一遍代码块里的代码,然后开始事件循环的。 puts "i am in run" 是在上面的 loop 之前执行的,所以只会执行一次。这里有个 EM 源码的简单分析 http://blog.yuaz.net/archives/441 供参考。

#4 楼 @zhangyuan https://github.com/igrigorik/em-http-request/blob/master/examples/fibered-http.rb 若使用类似上面的这种形式,

def action
  EM.run {
    fiber.new{...}.resume
  }
end

是否调用多少次 action 就有多少个 EM.run?

#6 楼 @towonzhou EM.run 会阻塞当前线程,所以不执行 EM.run {} 后边的代码。所以只能有一个事件循环。

慢慢写也不能太慢哦~~

sqsy 整理学习 EventMachine 的一些文章和帖子 提及了此话题。 09月08日 17:05

我是不是应该来催下更

需要 登录 后方可回复, 如果你还没有账号请 注册新账号