업데이트] 편집 : 쇼 작업 코드 은 또한 당신은 @client 변수가이 방법으로 사용되는 뮤텍스를 사용을 시도 할 수 있습니다. 디버깅 코드를 제외하고는 메인 모듈이 수정되지 않았습니다. 참고 : 해지하기 전에 구독 취소 필요성에 관해 이미 언급 한 문제를 경험했습니다.
코드가 올바르게 표시됩니다. 어떻게 인스턴스화하는지보고 싶습니다.
var ws = new WebSocket(uri);
당신이 인스턴스를 수행
require 'ws_communication'
config.middleware.use WsCommunication
그런 다음, 자바 스크립트 클라이언트에서,이 같은이 있어야합니다 설정/application.rb에서
, 당신은 아마 같은 최소한 뭔가를 WsCommunication의 또 다른 인스턴스? 그러면 @clients가 빈 배열로 설정되어 증상을 나타낼 수 있습니다. 이런 식으로 뭔가 잘못된 것 :
var ws = new WsCommunication;
그것은, 클라이언트를 표시 할 경우 우리를 도와 것이다 아마, 설정/application.rb이 게시물이 도움이되지 않는 경우.
덧붙여 말하자면, @clients가 모든 업데이트의 뮤텍스에 의해 보호되어야한다는 주석에 동의합니다. 이것은 이벤트 중심 시스템에서 언제든지 바뀔 수있는 동적 구조입니다. redis-mutex은 좋은 선택입니다. (Github이 현재 모든 것에 500 개의 오류를 던지고있는 것처럼 보이기 때문에 링크가 맞기를 바랍니다.)
$ redis.publish는 메시지를받은 클라이언트의 정수 값을 반환합니다.
마지막으로 종료하기 전에 채널의 구독을 취소해야 할 수도 있습니다. 나는 정리되지 않은 동일한 채널에 대한 이전 구독 때문에 각 메시지를 여러 번, 심지어 여러 번 보내는 상황을 경험했습니다. 스레드 내에서 채널을 구독 중이므로 같은 스레드 내에서 구독을 취소해야합니다. 그렇지 않으면 프로세스가 마술처럼 보이는 오른쪽 스레드를 기다리는 중 "멈춰"있습니다. 나는 "unsubscribe"플래그를 설정하고 메시지를 보내는 것으로 그 상황을 처리한다. 그런 다음 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
@kfrz 이것은 파이썬이 아니라 루비이다. 왜 다른 스레드에서이 작업을 수행하고 있습니까? 그리고 또 다른 질문입니다, 왜 당신은 이것을 손으로하고 있습니까? Rails 5.0을 사용한다면'actioncable'을 사용하면 모든 문제를 해결할 수 있습니다. – siegy22
도움이 필요하면 다음 페이지를 참조하십시오. http://stackoverflow.com/questions/16538732/share-variable-through-ruby-processes – DhruvPathak
@RaVeN Redis 코드가 차단됩니다. 결코 통제권을 반환하지 않습니다. 따라서 스레드가 작동해야합니다. ActionCable은 괜찮은 솔루션이며이 경우 모든 클라이언트가 JavaScript 인 경우 작동 할 수 있습니다. 그러나이 솔루션은보다 일반적이고 WebSocket을 지원하는 모든 클라이언트를 처리 할 수 있어야합니다. –