अंत में अद्यतन संपादन:कार्य कोड दिखाता है। डिबगिंग कोड को छोड़कर मुख्य मॉड्यूल असंशोधित। नोट:समाप्ति से पहले सदस्यता समाप्त करने की आवश्यकता के संबंध में मैंने पहले ही नोट कर ली समस्या का अनुभव किया।
कोड सही दिखता है। मैं देखना चाहता हूं कि आप इसे कैसे चालू कर रहे हैं।
config/application.rb में, शायद आपके पास कम से कम कुछ ऐसा हो:
require 'ws_communication'
config.middleware.use WsCommunication
फिर, आपके जावास्क्रिप्ट क्लाइंट में, आपके पास कुछ ऐसा होना चाहिए:
var ws = new WebSocket(uri);
क्या आप WsCommunication के किसी अन्य उदाहरण को तत्काल करते हैं? यह @clients को एक खाली सरणी में सेट करेगा और आपके लक्षण प्रदर्शित कर सकता है। कुछ इस तरह गलत होगा:
var ws = new WsCommunication;
यह हमारी मदद करेगा यदि आप क्लाइंट को दिखाएंगे और, शायद, config/application.rb अगर यह पोस्ट मदद नहीं करता है।
वैसे, मैं इस टिप्पणी से सहमत हूं कि किसी भी अपडेट पर @clients को म्यूटेक्स द्वारा संरक्षित किया जाना चाहिए, अगर यह भी नहीं पढ़ता है। यह एक गतिशील संरचना है जो किसी भी घटना-संचालित प्रणाली में किसी भी समय बदल सकती है। रेडिस-म्यूटेक्स एक अच्छा विकल्प है। (उम्मीद है कि लिंक सही है क्योंकि जीथब इस समय हर चीज पर 500 त्रुटियां फेंक रहा है।)
आप यह भी नोट कर सकते हैं कि $redis.publish संदेश प्राप्त करने वाले ग्राहकों की संख्या का एक पूर्णांक मान देता है।
अंत में, आप पा सकते हैं कि समाप्ति से पहले आपको यह सुनिश्चित करने की आवश्यकता है कि आपका चैनल सदस्यता समाप्त कर दिया गया है। मेरे पास ऐसी स्थितियां हैं जहां मैंने एक ही चैनल की पिछली सदस्यताओं के कारण प्रत्येक संदेश को एकाधिक, यहां तक कि कई बार भेजना समाप्त कर दिया है, जिन्हें साफ़ नहीं किया गया था। चूंकि आप एक थ्रेड के भीतर चैनल की सदस्यता ले रहे हैं, आपको उसी थ्रेड के भीतर सदस्यता समाप्त करने की आवश्यकता होगी या प्रक्रिया सही थ्रेड के जादुई रूप से प्रकट होने की प्रतीक्षा में बस "लटका" जाएगी। मैं "सदस्यता समाप्त" ध्वज सेट करके और फिर एक संदेश भेजकर उस स्थिति को संभालता हूं। फिर, on.message ब्लॉक के भीतर, मैं सदस्यता समाप्त ध्वज के लिए परीक्षण करता हूं और वहां सदस्यता समाप्त करता हूं।
आपके द्वारा प्रदान किया गया मॉड्यूल, केवल मामूली डिबगिंग संशोधनों के साथ:
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
मेरे द्वारा प्रदान किया गया परीक्षण ग्राहक कोड:
# 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
मेरे द्वारा प्रदान किया गया परीक्षण प्रकाशक कोड। प्रकाशक और सब्सक्राइबर को आसानी से जोड़ा जा सकता है, क्योंकि ये केवल परीक्षण हैं:
# 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
एक नमूना config.ru जो रैक मिडलवेयर परत पर यह सब चलाता है:
require './controllers/main'
require './middlewares/ws_communication'
use WsCommunication
run Main.new
यह मुख्य है। मैंने इसे अपने चल रहे संस्करण से हटा दिया है, इसलिए यदि आप इसका उपयोग करते हैं तो इसे ट्वीक करने की आवश्यकता हो सकती है:
%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