加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby-on-rails – 在Middleware中运行的线程正在使用旧版本的父

发布时间:2020-12-17 02:48:33 所属栏目:百科 来源:网络整理
导读:我用 Heroku tutorial来实现websockets. 它适用于Thin,但不适用于Unicorn和Puma. 还有一个回应消息实现,它响应客户端的消息.它在每个服务器上都能正常工作,因此websockets实现没有问题. Redis设置也是正确的(它捕获所有消息,并执行订阅块内的代码). 它现在如
我用 Heroku tutorial来实现websockets.

它适用于Thin,但不适用于Unicorn和Puma.

还有一个回应消息实现,它响应客户端的消息.它在每个服务器上都能正常工作,因此websockets实现没有问题.

Redis设置也是正确的(它捕获所有消息,并执行订阅块内的代码).

它现在如何工作:

在服务器启动时,初始化一个空的@clients数组.然后启动新的Thread,它正在侦听Redis,它旨在从@clients数组向相应的用户发送该消息.

在页面加载时,会创建新的websocket连接,它存储在@clients数组中.

如果我们从浏览器收到消息,我们会将其发送回与同一用户连接的所有客户端(该部分在Thin和Puma上都正常工作).

如果我们收到Redis的消息,我们也会查找存储在@clients数组中的所有用户连接.
这是奇怪的事情发生的地方:

>如果使用Thin运行,它会在@clients数组中找到连接并将消息发送给它们.
>如果使用Puma / Unicorn运行,@ clients数组总是为空,即使我们按该顺序尝试它(没有页面重新加载或任何东西):

>从浏览器发送消息 – > @ clients.length为1,传递消息
>通过Redis发送消息 – > @ clients.length为0,消息丢失
>从浏览器发送消息 – > @ clients.length仍为1,传递消息

有人可以澄清一下我错过了什么吗?

Puma服务器的相关配置:

workers 1
threads_count = 1
threads threads_count,threads_count

相关中间件代码:

require 'faye/websocket'

class NotificationsBackend

  def initialize(app)
    @app     = app
    @clients = []
    Thread.new do
      redis_sub = Redis.new
      redis_sub.subscribe(CHANNEL) do |on|
        on.message do |channel,msg|
          # logging @clients.length from here will always return 0
          # [..] retrieve user
          send_message(user.id,{ message: "ECHO: #{event.data}"} )
        end
      end
    end
  end

  def call(env)
    if Faye::WebSocket.websocket?(env)
      ws = Faye::WebSocket.new(env,nil,{ping: KEEPALIVE_TIME })
      ws.on :open do |event|
        # [..] retrieve current user
        if user
          # add ws connection to @clients array
        else
          # close ws
        end
      end

      ws.on :message do |event|
        # [..] retrieve current user
        Redis.current.publish({user_id: user.id,{ message: "ECHO: #{event.data}"}} )
      end

      ws.rack_response
    else
      @app.call(env)
    end
  end
  def send_message user_id,message
    # logging @clients.length here will always return correct result
    # cs = all connections which belong to that client
    cs.each { |c| c.send(message.to_json) }
  end
end

解决方法

Unicorn(显然是puma)都启动了一个主进程,然后分叉一个或多个worker. fork拷贝(或至少表示复制的假象 – 实际拷贝通常只在你写入页面时发生)整个进程,但只有在新进程中存在调用fork的线程.

很明显,您的应用程序在分叉之前正在初始化 – 这通常是为了让工作人员可以快速启动并从写入内存节省的副本中受益.因此,您的redis检查线程仅在主进程中运行,而@clients正在子进程中进行修改.

您可以通过推迟创建redis线程或禁用应用程序预加载来解决这个问题,但是您应该知道您的设置将阻止您扩展到单个工作进程之外(使用puma和线程友好的JVM,如jruby会少受约束)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读