gpt4 book ai didi

ruby-on-rails - 在中间件中运行的线程正在使用旧版本的父实例变量

转载 作者:行者123 更新时间:2023-12-04 23:44:55 26 4
gpt4 key购买 nike

我用过 Heroku tutorial实现websockets。

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

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

Redis 设置也是正确的(它捕获所有消息,并执行 subscribe 块中的代码)。

现在如何运作:

在服务器启动时,一个空 @clients数组被初始化。然后启动新线程,它正在监听 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)都启动了一个主进程,然后 fork 了一个或多个 worker 。 fork 复制(或至少呈现复制的错觉 - 实际复制通常仅在您写入页面时发生)您的整个过程,但只有调用 fork 的线程。存在于新进程中。

    很明显,您的应用程序在 fork 之前已被初始化 - 通常这样做是为了让工作人员可以快速启动并从写入时复制内存节省中受益。因此,您的 redis 检查线程仅在主进程中运行,而 @clients正在子进程中被修改。

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

    关于ruby-on-rails - 在中间件中运行的线程正在使用旧版本的父实例变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30762774/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com