4

I have a faye server (nodejs) running on localhost, and I am trying to setup a server side ruby client which needs to publish on the server on a regular basis. This is the code I am trying to use. (Please ignore the commented code to start with).
I make a class variable @@client and initialize it as soon as the class loads. I define a class method pub whose task is to publish something on the faye server.
In the end, I just call the pub method twice. The first publication callback is received successfully, but the second publication doesn't make either of callback or the errback. And since the control has not been given back to the app, the app just hangs there.
If I make the gobal variable $client (currently commented), the behaviour is the same. But if I make the client everytime pub is called, then the publish goes on smoothly. I initiate it in EM.run loop or outside, the behavior is same. (as expected)

I don't want to make a new connection everytime I want to publish something since that defeats the purpose. Also, if I create a new client in EM.run everytime I call the method, the client connections don't close by themselves. I can see them open in lsof command as open files, and soon I'll start getting too many open files error I think.

I don't really understand Event Machine correctly, maybe I am missing something there.

require 'faye'
require 'eventmachine'

# $client = Faye::Client.new('http://localhost:5050/faye')
class Fayeclient
  puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s

  # if !defined? @@client or @@client.nil?
    @@client = Faye::Client.new('http://localhost:5050/faye')
    puts "Created client: " + @@client.inspect
  # end

  def self.pub
    puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
    # client = Faye::Client.new('http://localhost:5050/faye') #$client
    # client = @@client
    EM.run {
      #client = Faye::Client.new('http://localhost:5050/faye') #$client
      puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
      puts @@client.inspect

      publication = @@client.publish('/foo', 'text' =>'Hello world')
      puts "Publishing: #{publication.inspect}"
      # puts "Publication methods: #{publication.methods}"

      publication.callback do
        puts "Did it #{publication.inspect}"
        EM.stop_event_loop
        puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
        # puts "#{client.methods}"
#        puts client.inspect
#        client.remove_all_listeners
#        puts client.inspect
      end
      publication.errback do |error |
          puts error.inspect
          EM.stop_event_loop
      end
    }
    puts "Outside event loop"
    puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
  end
end

Fayeclient.pub
Fayeclient.pub
neeraj
  • 1,191
  • 4
  • 19
  • 47
  • I can't help you with that specifically, but you might want to check out https://github.com/ryanb/private_pub. It's higher-level with secure channels, done entirely in Ruby. – Kites Sep 16 '14 at 13:27

1 Answers1

5

EM.run call is blocking, you have to run it on a separate thread, and eventually join it when all is over. In the example I'm using Singleton but it's up to you.

This does correctly the 2 faye calls.

#!/usr/bin/env ruby
#
require 'faye'
require 'singleton'
require 'eventmachine'


class Fayeclient
  include Singleton
  attr_accessor :em_thread, :client

  def initialize
    self.em_thread = Thread.new do
      EM.run
    end
    self.client = Faye::Client.new('http://localhost:8890/faye')
  end

  def pub
    puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
    puts client.inspect

    publication = client.publish('/foo', 'text' =>'Hello world')
    puts "Publishing: #{publication.inspect}"

    publication.callback do
      puts "Did it #{publication.inspect}"
      EM.stop_event_loop
      puts "#{__LINE__}: Reactor running: " + EM.reactor_running?.to_s
    end
    publication.errback do |error |
        puts error.inspect
        EM.stop_event_loop
    end
  end
end

Fayeclient.instance.pub
Fayeclient.instance.pub

Fayeclient.instance.em_thread.join

In my personal experience, anyway, having to deal with EventMachine inside Rails application can be a mess, some webserver uses EM, other does not so, and when you want to test from console it may not work as expected.

My solution is to fallback to http calls:

RestClient.post "http://localhost:#{Rails.configuration.faye_port}/faye", message: {foo: 'bar'}.to_json

I found this solution simpler and easy to customize, if you don't need to receive message from this piece of code.

Enrico Carlesso
  • 6,818
  • 4
  • 34
  • 42