Publishing messages inside a monitor (bind the publish event)

316 views
Skip to first unread message

slampis

unread,
Dec 8, 2011, 7:17:42 PM12/8/11
to Faye users
Hello, I am starting implementing a chat with faye but I am
experiencing some problem implementing the "presence" system.

What I am doing is the following:

When a user starts a new faye client, he also publishes a message on
the channel /presence/connect.
Then I monitor that event with the following code

faye_server.bind(:publish) do |client_id, channel, data|

if channel == '/presence/connect'
FayeUtils.client_connected :user_id => data['user_id'], :client_id
=> client_id
FayeUtils.update_contacts_list
end

end

where FayeUtils.client_connected handles all the logic.
FayeUtils.update_contacts_list tries to publish a new message to all
faye clients.

The missing part is the update of the online contacts list inside the
browsers.
If I try to publish data via a server client inside
FayeUtils.update_contacts_list, faye hangs.
It doesn't look like an infinite loop caused by recursion, but more
like if something goes in timeout.

Any idea on how to inspect that or is there any better way to
implement that?

Thank you.

James Coglan

unread,
Dec 13, 2011, 2:37:17 AM12/13/11
to faye-...@googlegroups.com
On 9 December 2011 00:17, slampis <stefan...@gmail.com> wrote:
faye_server.bind(:publish) do |client_id, channel, data|

 if channel == '/presence/connect'
   FayeUtils.client_connected :user_id => data['user_id'], :client_id
=> client_id
   FayeUtils.update_contacts_list
 end

end

where FayeUtils.client_connected handles all the logic.
FayeUtils.update_contacts_list tries to publish a new message to all
faye clients.

Can you publish all the code involved in this, or is that not possible? That would make it easier to debug. What I assume your code is doing is:

* client_connected() adds the user_id to a set and probably maps the user_id to its current client_id
* update_contacts_list() does something like, client.publish('/present', 'user_ids' => all_the_user_ids)

What type of client are you using to do this? Do you know how it's connected? Is it connected via WebSocket/HTTP or is the in-process faye_server.get_client connection?

Stefano Lampis

unread,
Dec 13, 2011, 2:48:18 AM12/13/11
to Faye users

On Dec 13, 8:37 am, James Coglan <jcog...@gmail.com> wrote:


> On 9 December 2011 00:17, slampis <stefanolam...@gmail.com> wrote:
>
> > faye_server.bind(:publish) do |client_id, channel, data|
>
> >  if channel == '/presence/connect'
> >    FayeUtils.client_connected :user_id => data['user_id'], :client_id
> > => client_id
> >    FayeUtils.update_contacts_list
> >  end
>
> > end
>
> > where FayeUtils.client_connected handles all the logic.
> > FayeUtils.update_contacts_list tries to publish a new message to all
> > faye clients.
>
> Can you publish all the code involved in this, or is that not possible?
> That would make it easier to debug. What I assume your code is doing is:
>
> * client_connected() adds the user_id to a set and probably maps the
> user_id to its current client_id
> * update_contacts_list() does something like, client.publish('/present',
> 'user_ids' => all_the_user_ids)

Your assumptions are right, if it helps I will post my code as I get
home.
For now, I can tell you that update_contacts_list does not use
client.publish but makes a direct HTTP POST to faye with restclient.
The restclient call is correct, I know because I use it in other
contexts.

>
> What type of client are you using to do this? Do you know how it's
> connected? Is it connected via WebSocket/HTTP or is the in-process
> faye_server.get_client connection?

By client you mean server client? If yes, any client: as I said I make
a simple HTTP request.
I don't understand the second question. How can I obtain that
information?

James Coglan

unread,
Dec 13, 2011, 2:59:37 AM12/13/11
to faye-...@googlegroups.com
On 13 December 2011 07:48, Stefano Lampis <stefan...@gmail.com> wrote:
For now, I can tell you that update_contacts_list does not use
client.publish but makes a direct HTTP POST to faye with restclient.
The restclient call is correct, I know because I use it in other
contexts.

This is just a guess, but it sounds like what might be happening is that you're making a blocking call to Faye (a single-threaded, event-driven server) while it's in the middle of trying to respond to another request. Try either using `faye_server.get_client.publish(...)` to publish your data, or use an evented HTTP client like em-http-request, or the new faye-websocket library. 

Stefano Lampis

unread,
Dec 13, 2011, 3:22:41 AM12/13/11
to Faye users
This sounds reasonable because I am starting faye with something like

rackup faye.ru

which contains a line like

bayeux = Faye::RackAdapter.new(:mount => '/faye', :timeout => 25)

and then something like

run bayeux

As you can see my code is based on the basic example.
Can you confirm that this approach starts a single threaded server?

I've given a quick look to faye-websocket. I understand that it would
be easy to implement a simple server, but my question is?
Can I still use the faye javascript client?
If yes, do I lost compatibility with browsers which are not supporting
web sockets?

On Dec 13, 8:59 am, James Coglan <jcog...@gmail.com> wrote:

James Coglan

unread,
Dec 13, 2011, 3:26:30 AM12/13/11
to faye-...@googlegroups.com
On 13 December 2011 08:22, Stefano Lampis <stefan...@gmail.com> wrote:
This sounds reasonable because I am starting faye with something like

rackup faye.ru

which contains a line like

bayeux = Faye::RackAdapter.new(:mount => '/faye', :timeout => 25)

and then something like

run bayeux

As you can see my code is based on the basic example.
Can you confirm that this approach starts a single threaded server?

You need to use Thin to serve Faye, since it uses async responses. To do this, run

rackup -s thin -E production faye.ru
 
I've given a quick look to faye-websocket. I understand that it would
be easy to implement a simple server, but my question is?
Can I still use the faye javascript client?
If yes, do I lost compatibility with browsers which are not supporting
web sockets?

faye-websocket is *only* a WebSocket implementation. It's not a shim, an abstraction, or a higher-level messaging service. It's just the WebSocket-handling code I've extracted from Faye. If you start a faye-websocket server, you can connect to it using the WebSocket/MozWebSocket interfaces in browsers.

Stefano Lampis

unread,
Dec 14, 2011, 5:23:58 AM12/14/11
to Faye users
I've checked out my scripts and this is the exact command I use to
start faye

rackup faye.ru -D -s thin -E production -p 9292

but still, it hangs under the conditions I have described.

If it helps, I am using ruby EE, so it's ruby 1.8.7

On Dec 13, 9:26 am, James Coglan <jcog...@gmail.com> wrote:

James Coglan

unread,
Dec 14, 2011, 7:26:40 AM12/14/11
to faye-...@googlegroups.com
On 14 December 2011 10:23, Stefano Lampis <stefan...@gmail.com> wrote:
I've checked out my scripts and this is the exact command I use to
start faye

rackup faye.ru -D -s thin -E production -p 9292

but still, it hangs under the conditions I have described.

I can confirm that making a blocking call blocks Thin: run this:

require 'rack'
require 'uri'
require 'net/http'

app = lambda do |env|
  body = case env['PATH_INFO']
         when '/hi'
           uri = URI.parse('http://localhost:8000/')
           Net::HTTP.get_response(uri).body
         else
           'Hello!'
         end

  [200, {'Content-Type' => 'text/plain'}, [body]]
end

handler = Rack::Handler.get('thin')
handler.run(app, :Port => 8000)

If you request http://localhost:8000/hi, the server hangs. This is because you deadlock the server by blocking the event loop with an HTTP call that can never return, because the server it's calling has its event loop blocked. If that makes sense.

Use the Faye client to publish messages, or an async http/websocket client, and you should be fine.

Stefano Lampis

unread,
Dec 14, 2011, 7:44:15 AM12/14/11
to Faye users
Using the faye client worked like a charm.
Many thanks for your help

On Dec 14, 1:26 pm, James Coglan <jcog...@gmail.com> wrote:


> On 14 December 2011 10:23, Stefano Lampis <stefanolam...@gmail.com> wrote:
>
> > I've checked out my scripts and this is the exact command I use to
> > start faye
>
> > rackup faye.ru -D -s thin -E production -p 9292
>
> > but still, it hangs under the conditions I have described.
>
> I can confirm that making a blocking call blocks Thin: run this:
>
> require 'rack'
> require 'uri'
> require 'net/http'
>
> app = lambda do |env|
>   body = case env['PATH_INFO']
>          when '/hi'
>            uri = URI.parse('http://localhost:8000/')
>            Net::HTTP.get_response(uri).body
>          else
>            'Hello!'
>          end
>
>   [200, {'Content-Type' => 'text/plain'}, [body]]
> end
>
> handler = Rack::Handler.get('thin')
> handler.run(app, :Port => 8000)
>

> If you requesthttp://localhost:8000/hi, the server hangs. This is because

Jerome B

unread,
Dec 19, 2011, 5:11:48 AM12/19/11
to Faye users
Hello,

Can you provide an example for use Faye client in server events?
I need to use a client in server subscribe event but I'm not sure of
the right way to achieve this.

Thank you

James Coglan

unread,
Dec 19, 2011, 5:15:54 AM12/19/11
to faye-...@googlegroups.com
On 19 December 2011 10:11, Jerome B <blanchar...@gmail.com> wrote:
Can you provide an example for use Faye client in server events?
I need to use a client in server subscribe event but I'm not sure of
the right way to achieve this.

Can you clarify where exactly you're trying to use the client, and maybe some example code? Is this inside your application code, or in a Faye extension, or a Faye event listener? Are you on Ruby or Node?

Stefano Lampis

unread,
Dec 19, 2011, 5:16:01 AM12/19/11
to faye-...@googlegroups.com
This is very simple, just use the self.get_client method to obtain the ruby client. Then you can publish whatever you want.

self.get_client.publish '/channel/name', { ... }

--
Stefano Lampis

Jerome B

unread,
Dec 19, 2011, 5:29:17 AM12/19/11
to Faye users
Thanks for your answers!

I try to use it in my server:

faye_server.bind(:subscribe) do |client_id, channel|
# USE CLIENT HERE
end

So can I do something like faye_server.get_client.publish here ?

Thanks

On Dec 19, 11:16 am, Stefano Lampis <stefanolam...@gmail.com> wrote:
> This is very simple, just use the self.get_client method to obtain the ruby
> client. Then you can publish whatever you want.
>
> self.get_client.publish '/channel/name', { ... }
>
> --
> Stefano Lampis
>

James Coglan

unread,
Dec 19, 2011, 5:31:19 AM12/19/11
to faye-...@googlegroups.com
On 19 December 2011 10:29, Jerome B <blanchar...@gmail.com> wrote:
faye_server.bind(:subscribe) do |client_id, channel|
 # USE CLIENT HERE
end

So can I do something like faye_server.get_client.publish here ?

Yep, that should work. 

Jerome B

unread,
Dec 19, 2011, 6:43:25 AM12/19/11
to Faye users
Yes it works!

Thank you very much for the answers.

Jerome

On Dec 19, 11:31 am, James Coglan <jcog...@gmail.com> wrote:

Reply all
Reply to author
Forward
0 new messages