10

以下是我的赛璐珞代码。

  1. client1.rb2 位客户之一。(我将其命名为客户端 1)

  2. client2.rb2 个客户中的第 2 个。(命名为客户端 2)

笔记:

上述 2 个客户端之间的唯一区别是传递给服务器的文本。即('client-1''client-2'分别)

在针对以下 2 个服务器(一次一个)测试这 2 个客户端(通过并排运行它们)时。我发现很奇怪的结果

  1. server1.rb取自赛璐珞-zmq 的README.md 的基本示例

    将其用作上述 2 个客户端的示例服务器会导致任务的并行执行

输出

ruby server1.rb

Received at 04:59:39 PM and message is client-1
Going to sleep now
Received at 04:59:52 PM and message is client-2

笔记:

当client1.rb请求处于睡眠状态时,client2.rb消息被处理。(并行标记

  1. server2.rb

    将其用作上述 2 个客户端的示例服务器并没有导致任务的并行执行

输出

ruby server2.rb

Received at 04:55:52 PM and message is client-1
Going to sleep now
Received at 04:56:52 PM and message is client-2

笔记:

客户端2 被要求等待 60 秒,因为客户端 1正在睡眠(60 秒睡眠)

我多次运行上述测试都导致相同的行为。

谁能从上述测试的结果中解释一下。

问题:为什么赛璐珞要等待 60 秒才能处理其他请求,即在 server2.rb 案例中注意到的那样。?

红宝石版本

ruby -v

ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]

4

2 回答 2

6

使用你的要点,我验证了这个问题可以在MRI 2.2.1as jRuby 1.7.21and and ... 中重现。and 之间的Rubinius 2.5.8区别在于后者中使用了and类方法。server1.rbserver2.rbDisplayMessagemessage


sleepin的使用DisplayMessage超出了Celluloid范围。

sleep被使用时,server1.rbCelluloid.sleep实际上是在使用,但当被使用时,server2.rb它正在使用Kernel.sleep... 锁定邮箱Server直到 60 秒过去。这可以防止在邮箱再次处理消息(对actor的方法调用)之前处理对该actor的未来方法调用。

有三种方法可以解决这个问题:

  • 使用一个defer {}future {}块。

  • 显式调用Celluloid.sleep而不是sleep(如果没有显式调用 as Celluloid.sleep, usingsleep将最终调用Kernel.sleepsince DisplayMessagedoes not include Celluloidlike Serverdoes )

  • 将 into 的内容DisplayMessage.message带入; 或至少 into ,它在范围内,并且将使用正确的.handle_messageserver1.rbServerCelluloidsleep


defer {}方法:

def handle_message(message)
  defer {
    DisplayMessage.message(message)
  }
end

Celluloid.sleep方法:

class DisplayMessage
    def self.message(message)
      #de ...
      Celluloid.sleep 60
    end
end

不是真正的范围问题;这是关于异步的。

重申一下,更深层次的问题不是sleep……的范围,这就是为什么deferfuture也是我最好的建议。但是在这里发布我的评论中出现的内容:

使用deferfuture推送会导致参与者被绑定到另一个线程的任务。如果你使用future,你可以在任务完成后获得返回值,如果你使用,defer你可以触发并忘记。

但更好的是,为容易被捆绑的任务创建另一个演员,甚至汇集其他演员......如果deferfuture不适合你。

我很乐意回答这个问题提出的后续问题;我们有一个非常活跃的邮件列表和 IRC 频道。你慷慨的赏金是值得称赞的,但我们中的很多人会纯粹为了帮助你而提供帮助。

于 2016-01-30T17:52:18.463 回答
3

设法重现并解决了这个问题。删除我之前的回答。显然,问题出在sleep. 通过将日志添加到Celluloids.rb's"actor/kernel sleeping"的本地副本来确认。sleep()


server1.rb,

调用sleepserver- 一个包含赛璐珞的类。

因此,赛璐珞的实现sleep覆盖了原生的sleep.

class Server
  include Celluloid::ZMQ

  ...

  def run
    loop { async.handle_message @socket.read }
  end

  def handle_message(message)

        ...

        sleep 60
  end
end

actor sleeping注意来自的日志server1.rb添加到Celluloids.rb 的日志sleep()

这仅暂停赛璐珞中的当前“演员”,即只有当前处理 client1 的“赛璐珞线程”休眠。


server2.rb,

调用sleepDisplayMessage是在不包括赛璐珞的不同类中。

因此,它是本地人sleep本身。

class DisplayMessage
    def self.message(message)

           ...

           sleep 60
    end
end

注意任何actor sleeping日志的缺失server2.rb

这会暂停当前的 ruby​​ 任务,即 ruby​​ 服务器休眠(不仅仅是单个赛璐珞演员)。


修复?

在中,必须明确指定server2.rb适当的。sleep

class DisplayMessage
    def self.message(message)
        puts "Received at #{Time.now.strftime('%I:%M:%S %p')} and message is #{message}"
        ## Intentionally added sleep to test whether Celluloid block the main process for 60 seconds or not.
        if message == 'client-1'
           puts 'Going to sleep now'.red

           # "sleep 60" will invoke the native sleep.
           # Use Celluloid.sleep to support concurrent execution
           Celluloid.sleep 60
        end
    end
end
于 2016-01-30T18:12:13.253 回答