如何在使用Sinatra Server Sent Events流时提高并发性

时间:2013-08-05 16:59:33

标签: sinatra eventmachine thin server-sent-events puma

我正在构建一个订阅Redis频道的Rack中间件,并使用Server Sent Events将消息推送到客户端。 Sinatra provides a nice DSL for doing this.我有一个有效的例子,然而,我遇到的问题是,一旦到达7或8个客户端,性能就会大幅下降。在尝试在请求之间重用Redis连接时,我也遇到了“死锁”服务器的问题。

我正在使用Thin来为应用程序提供服务(它使用了EventMachine)。我认为Sinatra DSL已经使用EventMachine处理了并发性,但也许这是我需要自己实现的东西?我不想仅限于基于EventMachine的服务器(Thin,Rainbows!),以防有人想要使用像Puma这样的多线程服务器。我该怎么做才能增加代码中的并发性?

require 'redis'
require 'sinatra/base'

class SSE < Sinatra::Base

  def send_message(json)
    "id: #{Time.now}\n" +
    "data: #{json}" +
    "\r\n\n"
  end

  get '/channels/:id/subscribe', provides: 'text/event-stream' do
    channel_id = params['id']
    stream(:keep_open) do |connection|
      Redis.new.subscribe("channels:#{channel_id}") do |on|
        on.message do |channel, json|
          connection << send_message(json)
        end
      end
    end
  end

end

2 个答案:

答案 0 :(得分:1)

我想到了一些事情,所以我会按顺序迭代这些事情。

  

我正在使用Thin来提供应用程序(它使用的是EventMachine   罩)。我认为Sinatra DSL已经处理了并发性   使用EventMachine,但也许这是我需要的东西   实施自己?

你是对的,Thin使用EventMachine。但是,使用EventMachine(或任何其他反应堆)的是,一旦执行同步操作,就会停止整个反应堆。因此,要真正获得并发性,您需要在整个应用程序中继续使用EventMachine。

结帐em-hiredis以获取支持pub / sub的EventMachine ready Redis客户端。

  

我不想仅限于基于EventMachine的服务器   (Thin,Rainbows!)以防有人想要使用多线程服务器   像Puma一样

从未尝试过我要说的内容,但我不认为在服务器中使用EventMachine时会遇到问题。记住要开始自己的EM。也许在config.ru?

  

我在尝试时遇到了“死锁”服务器的问题   在请求之间重用Redis连接

我相信您遇到这种情况的原因是因为每次调用'/ channels /:id / subscribe'都会打开与Redis的新连接。你只能打开那么多人。考虑将Redis.new重构为应用程序的共享连接。只打开一次。单个Redis连接应该能够处理多个pub / subs。

只是一些想法,我希望他们有所帮助。

答案 1 :(得分:1)

经过大量的研究和实验,这里是我与sinatra + sinatra sse gem一起使用的代码:

class EventServer < Sinatra::Base
 include Sinatra::SSE
 set :connections, []
 .
 .
 .
 get '/channel/:channel' do
 .
 .
 .
  sse_stream do |out|
    settings.connections << out
    out.callback {
      puts 'Client disconnected from sse';
      settings.connections.delete(out);
    }
  redis.subscribe(channel) do |on|
      on.subscribe do |channel, subscriptions|
        puts "Subscribed to redis ##{channel}\n"
      end
      on.message do |channel, message|
        puts "Message from redis ##{channel}: #{message}\n"
        message = JSON.parse(message)
        .
        .
        .
        if settings.connections.include?(out)
          out.push(message)
        else
          puts 'closing orphaned redis connection'
          redis.unsubscribe
        end
      end
    end
  end
end

redis连接阻止on.message并且只接受(p)subscribe /(p)unsubscribe命令。取消订阅后,redis连接不再被阻止,并且可以由初始sse请求实例化的Web服务器对象释放。当您在redis上收到消息时,它会自动清除,并且集合数组中不再存在与浏览器的连接。