DIPERBARUI EDIT AT AKHIR:Menunjukkan kode kerja. Modul utama tidak dimodifikasi kecuali untuk kode debug. Catatan:Saya memang mengalami masalah yang telah saya catat tentang perlunya berhenti berlangganan sebelum penghentian.
Kode terlihat benar. Saya ingin melihat bagaimana Anda membuat instance-nya.
Di config/application.rb, Anda mungkin memiliki setidaknya sesuatu seperti:
require 'ws_communication'
config.middleware.use WsCommunication
Kemudian, di klien JavaScript Anda, Anda harus memiliki sesuatu seperti ini:
var ws = new WebSocket(uri);
Apakah Anda membuat instance lain dari WsCommunication? Itu akan mengatur @clients ke array kosong dan dapat menunjukkan gejala Anda. Sesuatu seperti ini akan salah:
var ws = new WsCommunication;
Akan membantu kami jika Anda akan menunjukkan klien dan, mungkin, config/application.rb jika posting ini tidak membantu.
Omong-omong, saya setuju dengan komentar bahwa @clients harus dilindungi oleh mutex pada pembaruan apa pun, jika tidak dibaca juga. Ini adalah struktur dinamis yang dapat berubah kapan saja dalam sistem yang digerakkan oleh peristiwa. redis-mutex adalah pilihan yang baik. (Semoga tautan itu benar karena Github tampaknya membuat 500 kesalahan pada semua hal saat ini.)
Anda mungkin juga memperhatikan bahwa $redis.publish mengembalikan nilai bilangan bulat dari jumlah klien yang menerima pesan.
Terakhir, Anda mungkin perlu memastikan bahwa saluran Anda dihentikan langganannya sebelum dihentikan. Saya pernah mengalami situasi di mana saya akhirnya mengirim setiap pesan beberapa kali, bahkan berkali-kali, karena langganan sebelumnya ke saluran yang sama yang tidak dibersihkan. Karena Anda berlangganan saluran di dalam utas, Anda harus berhenti berlangganan di utas yang sama atau prosesnya hanya akan "menggantung" menunggu utas yang tepat muncul secara ajaib. Saya menangani situasi itu dengan menetapkan bendera "berhenti berlangganan" dan kemudian mengirim pesan. Kemudian, di dalam blok on.message, saya menguji tanda berhenti berlangganan dan mengeluarkan tanda berhenti berlangganan di sana.
Modul yang Anda berikan, dengan hanya sedikit modifikasi debug:
require 'faye/websocket'
require 'redis'
class WsCommunication
KEEPALIVE_TIME = 15 #seconds
CHANNEL = 'vip-deck'
def initialize(app)
@app = app
@clients = []
uri = URI.parse(ENV['REDISCLOUD_URL'])
$redis = Redis.new(host: uri.host, port: uri.port, password: uri.password)
Thread.new do
redis_sub = Redis.new(host: uri.host, port: uri.port, password: uri.password)
redis_sub.subscribe(CHANNEL) do |on|
on.message do |channel, msg|
puts "Message event. Clients receiving:#{@clients.count};"
@clients.each { |ws| ws.send(msg) }
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|
@clients << ws
puts "Open event. Clients open:#{@clients.count};"
end
ws.on :message do |event|
receivers = $redis.publish(CHANNEL, event.data)
puts "Message published:#{event.data}; Receivers:#{receivers};"
end
ws.on :close do |event|
@clients.delete(ws)
puts "Close event. Clients open:#{@clients.count};"
ws = nil
end
ws.rack_response
else
@app.call(env)
end
end
end
Kode pelanggan uji yang saya berikan:
# encoding: UTF-8
puts "Starting client-subscriber.rb"
$:.unshift File.expand_path '../lib', File.dirname(__FILE__)
require 'rubygems'
require 'eventmachine'
require 'websocket-client-simple'
puts "websocket-client-simple v#{WebSocket::Client::Simple::VERSION}"
url = ARGV.shift || 'ws://localhost:3000'
EM.run do
ws = WebSocket::Client::Simple.connect url
ws.on :message do |msg|
puts msg
end
ws.on :open do
puts "-- Subscriber open (#{ws.url})"
end
ws.on :close do |e|
puts "-- Subscriber close (#{e.inspect})"
exit 1
end
ws.on :error do |e|
puts "-- Subscriber error (#{e.inspect})"
end
end
Kode penerbit uji yang saya berikan. Penerbit dan Pelanggan dapat dengan mudah digabungkan, karena ini hanyalah pengujian:
# encoding: UTF-8
puts "Starting client-publisher.rb"
$:.unshift File.expand_path '../lib', File.dirname(__FILE__)
require 'rubygems'
require 'eventmachine'
require 'json'
require 'websocket-client-simple'
puts "websocket-client-simple v#{WebSocket::Client::Simple::VERSION}"
url = ARGV.shift || 'ws://localhost:3000'
EM.run do
count ||= 0
timer = EventMachine.add_periodic_timer(5+rand(5)) do
count += 1
send({"MESSAGE": "COUNT:#{count};"})
end
@ws = WebSocket::Client::Simple.connect url
@ws.on :message do |msg|
puts msg
end
@ws.on :open do
puts "-- Publisher open"
end
@ws.on :close do |e|
puts "-- Publisher close (#{e.inspect})"
exit 1
end
@ws.on :error do |e|
puts "-- Publisher error (#{e.inspect})"
@ws.close
end
def self.send message
payload = message.is_a?(Hash) ? message : {payload: message}
@ws.send(payload.to_json)
end
end
Contoh config.ru yang menjalankan semua ini di lapisan middleware rak:
require './controllers/main'
require './middlewares/ws_communication'
use WsCommunication
run Main.new
Ini adalah Utama. Saya menghapusnya dari versi saya yang sedang berjalan sehingga mungkin perlu diubah jika Anda menggunakannya:
%w(rubygems bundler sinatra/base json erb).each { |m| require m }
ENV['RACK_ENV'] ||= 'development'
Bundler.require
$: << File.expand_path('../', __FILE__)
$: << File.expand_path('../lib', __FILE__)
Dir["./lib/*.rb", "./lib/**/*.rb"].each { |file| require file }
env = ENV['OS'] == 'Windows_NT' ? 'development' : ENV['RACK_ENV']
class Main < Sinatra::Base
env = ENV['OS'] == 'Windows_NT' ? 'development' : ENV['RACK_ENV']
get "/" do
erb :"index.html"
end
get "/assets/js/application.js" do
content_type :js
@scheme = env == "production" ? "wss://" : "ws://"
erb :"application.js"
end
end