sql >> Database >  >> NoSQL >> Redis

Toegang krijgen tot een variabele binnen een railsthread

BIJGEWERKT BEWERKEN AAN EINDE:Toont werkende code. Hoofdmodule ongewijzigd, behalve voor foutopsporingscode. Opmerking:ik heb het probleem ervaren dat ik al heb opgemerkt met betrekking tot de noodzaak om je af te melden voorafgaand aan de beëindiging.

De code ziet er correct uit. Ik zou graag willen zien hoe je het concretiseert.

In config/application.rb heb je waarschijnlijk iets als:

require 'ws_communication'
config.middleware.use WsCommunication

Dan zou je in je JavaScript-client zoiets als dit moeten hebben:

var ws = new WebSocket(uri);

Instantieer je een ander exemplaar van WsCommunication? Dat zou @clients op een lege array plaatsen en uw symptomen kunnen vertonen. Iets als dit zou onjuist zijn:

var ws = new WsCommunication;

Het zou ons helpen als je de client en misschien config/application.rb zou laten zien als dit bericht niet helpt.

Overigens ben ik het eens met de opmerking dat @clients bij elke update door een mutex moeten worden beschermd, zo niet ook. Het is een dynamische structuur die op elk moment kan veranderen in een gebeurtenisgestuurd systeem. redis-mutex is een goede optie. (Ik hoop dat die link correct is, want Github lijkt op dit moment 500 fouten op alles te gooien.)

U kunt ook opmerken dat $redis.publish een geheel getal retourneert van het aantal clients dat het bericht heeft ontvangen.

Ten slotte kan het zijn dat je ervoor moet zorgen dat je kanaal vóór beëindiging is uitgeschreven. Ik heb situaties gehad waarin ik elk bericht meerdere, zelfs vele keren heb verzonden vanwege eerdere abonnementen op hetzelfde kanaal die niet waren opgeschoond. Aangezien je je abonneert op het kanaal binnen een thread, moet je je afmelden binnen diezelfde thread, anders blijft het proces gewoon "hangen" totdat de juiste thread op magische wijze verschijnt. Ik handel die situatie af door een "unsubscribe"-vlag in te stellen en vervolgens een bericht te verzenden. Vervolgens test ik binnen het on.message-blok op de afmeldvlag en geef ik daar de afmelding uit.

De module die u heeft geleverd, met slechts kleine debugging-aanpassingen:

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

De testabonneecode die ik heb verstrekt:

# 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

De testuitgeverscode die ik heb verstrekt. Uitgever en Abonnee kunnen gemakkelijk worden gecombineerd, aangezien dit slechts tests zijn:

# 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

Een voorbeeld van config.ru die dit allemaal uitvoert op de rack-middleware-laag:

require './controllers/main'
require './middlewares/ws_communication'
use WsCommunication
run Main.new

Dit is Main. Ik heb het uit mijn actieve versie verwijderd, dus het moet misschien worden aangepast als je het gebruikt:

%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


  1. MongoDB + C#-stuurprogramma + query-array van elementen waarbij elk array-element een subdocument bevat om op te zoeken

  2. 5 manieren om de maand van een datum te krijgen in MongoDB

  3. Voorwaardelijke $ som in MongoDB

  4. Schakel redis uit bij veel time-outs met spring boot