Я использовал Heroku tutorial для реализации веб-узлов.Thread running in Middleware использует старую версию переменной экземпляра родителя
Он работает правильно с тонким, но не работает с Единорогом и Пумой.
Также реализовано эхо-сообщение, которое отвечает на сообщение клиента. Он работает правильно на каждом сервере, поэтому нет проблем с реализацией websockets.
Настройка Redis также верна (она захватывает все сообщения и выполняет код внутри блока subscribe
).
Как это работает в настоящее время:
На старте сервера, пустой @clients
массив инициализируется. Затем запускается новый поток, который прослушивает Redis и который предназначен для отправки этого сообщения соответствующему пользователю из массива @clients.
Нагрузка на страницу создается новое соединение с веб-соединением, оно хранится в массиве @clients.
Если мы получим сообщение от браузера, мы отправим его всем клиентам, связанным с одним и тем же пользователем (эта часть работает правильно как с Thin, так и с Puma).
Если мы получим сообщение от Redis, мы также рассмотрим все подключения пользователя, хранящиеся в массиве @clients. Это где странная вещь происходит:
При работе с тонкими, он находит связи в @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
если вы регистрируете идентификатор процесса, когда ваш поток redis получает событие, а когда вы изменяете @clients, вы получаете то же значение? –
@FrederickCheung только что проверил, они разные. Метод Initialize и поток прослушивателя Redis имеют одинаковый PID, но он отличается (ниже), чем тот, где изменен '@ clients'. BTW все клиенты хранятся в одном и том же процессе (все они принадлежат одному массиву PID и '@ clients') –