Question on listening to events with PubSub in js

314 views
Skip to first unread message

Clemens

unread,
Feb 8, 2012, 4:36:36 AM2/8/12
to Autobahn
I am working on an application where webclients can load some data
from a Python server and listen for updates to the data and render the
changes accordingly.
The problem I have run in to is that the clients doesn't react to the
events send from the server immediately.
Consider the following js code:

var sess = null;

$(document).ready(function()
{
self.sess = new ab.Session("ws://localhost:9000", function() {
ab.log("Connected!");
self.sess.prefix("event", "http://www.example.com/events#");
self.sess.subscribe("event:update", onContentUpdated);
sess.prefix("rpc", "http://www.example.com/rpc#");
});
});

function onContentUpdated(topicUri, event) {
console.log("Update");
}

function load()
{
sess.call("rpc:load").always(ab.log);
}

As I understand the framework the 'onContentUpdated' function should
now be called when an update event arrives from the server.
This is however not the case. When the server sends the events nothing
happens. However if the 'load' function is called (e.g. through a
button press on a webpage) the 'onContentUpdated' is called based on
all the events previously dispatched from the server.
Is there something regarding PubSub I have misunderstood? I have
tested the code both in Safari, Chrome and Firefox.

Thanks!
Clemens

Tobias Oberstein

unread,
Feb 8, 2012, 4:52:23 AM2/8/12
to autob...@googlegroups.com, Clemens
Hello Clemens,

a PubSub event published (either on server or from other client and
dispatched via server) will be dispatched to all connected clients
immediately.

The server does not buffer events for clients that are currently
not connected (and only connect and subscribe later).

You can check out this demo

https://github.com/oberstet/Autobahn/tree/master/demo/pubsub/simple

to verify this.

==

Regarding your code below, I'm not sure if the "self." stuff does
any harm .. try removing it.

==

You can turn on debug output to see whats going on:

In JS, insert

ab.debug(true);

at the very beginning.

In Python,

factory = WampServerFactory("ws://localhost:9000", debugWamp = True)

will make the server log any client connects, subscriptions and
event dispatching.

Hope this helps .. if not, pls come back ..
\Tobias

Clemens

unread,
Feb 8, 2012, 5:10:59 AM2/8/12
to Autobahn
Hi Tobias,
Thank you for the prompt answer!
Sorry about the self stuff, I was mixed up in Python when I wrote the
example js :-)

I've now tried to enable debug both on server-side and client side.
On the server side I see the event being dispatched, yet still nothing
happens in the console on the client side.
However if I trigger the load function in the above code I will
receive the events that has been previously dispatched from the server
and get a number of the following messages in the prompt

WAMP Event
autobahn-latest.js:281ws://localhost:9000 [2B2QWo8y2HOOCc3S]
autobahn-latest.js:282http://www.example.com/events#update
autobahn-latest.js:283
Object

Thanks again,
Clemens

On Feb 8, 10:52 am, Tobias Oberstein <tobias.oberst...@gmail.com>
wrote:

Tobias Oberstein

unread,
Feb 8, 2012, 6:08:09 AM2/8/12
to autob...@googlegroups.com, Clemens
sounds strange .. i'd suspect you might block your browser UI in
some of your JS code .. however I can't tell: can you pastebin
your client/server code?

alos, pls first try out the simple pubsub demo and report back if
that works for you ..

also: can you attach the complete log output of the server?

Clemens

unread,
Feb 8, 2012, 6:27:42 AM2/8/12
to Autobahn
Hi again,
By testing the pubsub demo and modifying it a bit match my events I
can see that there are no problems in my client.
However it seems the problem lies in the server.
When the server calls factory._dispatchEvent("http://www.example.com/
event#updated", data) the event is not dispatched until something else
happens – e.g. when another client connects.
It might be a threading issue. I am going to investigate it a bit...

Thank you so far,
Clemens

On Feb 8, 12:08 pm, Tobias Oberstein <tobias.oberst...@gmail.com>
wrote:

Tobias Oberstein

unread,
Feb 8, 2012, 6:37:18 AM2/8/12
to autob...@googlegroups.com, Clemens
Hi Clemens,

ok;)

Just to avoid misunderstandings:

Autobahn server itself of course is capable of dispatching events
asynchronously .. for multiple clients, multiple concurrent dispatches
etc etc.

We've tested that with 180k connected clients and dispatching events
to them at >20k msgs per sec ..

However, if you use threads on your server, you must make sure not
to block the Twisted reactor (the event loop under the hood).

So for example, you likely want to use deferToThread (or something like)

Have a look at

http://twistedmatrix.com/documents/current/core/howto/gendefer.html#auto5

http://twistedmatrix.com/documents/current/core/howto/threading.html

==

All this is not Autobahn, but Twisted specific.

In general: using threads with asynch. frameworks like Twisted is
something to be avoided, unless absolutely necessary (read: you
need to integrate legacy, blocking code).

Hoep this helps,

\Tobias


Am 08.02.2012 12:27, schrieb Clemens:
> Hi again,
> By testing the pubsub demo and modifying it a bit match my events I
> can see that there are no problems in my client.
> However it seems the problem lies in the server.
> When the server calls factory._dispatchEvent("http://www.example.com/
> event#updated", data) the event is not dispatched until something else

> happens � e.g. when another client connects.

Clemens

unread,
Feb 8, 2012, 6:53:47 AM2/8/12
to Autobahn
Hi again Tobias,
I was starting to suspect that I was doing something messy :-)

I managed to get it working by replacing the following line:

factory._dispatchEvent("http://www.example.com/event#updated", data)

with:

reactor.callFromThread(self.factory._dispatchEvent, "http://
www.example.com/event#updated", data)

The problem was that the '_dispatchEvent' function was called from a
worker thread.

Thanks for the help, the fast reponse, and a great library :-)

- Clemens

On Feb 8, 12:37 pm, Tobias Oberstein <tobias.oberst...@gmail.com>
wrote:
> Hi Clemens,
>
> ok;)
>
> Just to avoid misunderstandings:
>
> Autobahn server itself of course is capable of dispatching events
> asynchronously .. for multiple clients, multiple concurrent dispatches
> etc etc.
>
> We've tested that with 180k connected clients and dispatching events
> to them at >20k msgs per sec ..
>
> However, if you use threads on your server, you must make sure not
> to block the Twisted reactor (the event loop under the hood).
>
> So for example, you likely want to use deferToThread (or something like)
>
> Have a look at
>
> http://twistedmatrix.com/documents/current/core/howto/gendefer.html#a...
>
> http://twistedmatrix.com/documents/current/core/howto/threading.html
>
> ==
>
> All this is not Autobahn, but Twisted specific.
>
> In general: using threads with asynch. frameworks like Twisted is
> something to be avoided, unless absolutely necessary (read: you
> need to integrate legacy, blocking code).
>
> Hoep this helps,
>
> \Tobias
>
> Am 08.02.2012 12:27, schrieb Clemens:
>
>
>
>
>
>
>
> > Hi again,
> > By testing the pubsub demo and modifying it a bit match my events I
> > can see that there are no problems in my client.
> > However it seems the problem lies in the server.
> > When the server calls factory._dispatchEvent("http://www.example.com/
> > event#updated", data) the event is not dispatched until something else
> > happens e.g. when another client connects.

Tobias Oberstein

unread,
Feb 8, 2012, 7:01:58 AM2/8/12
to autob...@googlegroups.com, Clemens
Am 08.02.2012 12:53, schrieb Clemens:
> The problem was that the '_dispatchEvent' function was called from a
> worker thread.
>
> Thanks for the help, the fast reponse, and a great library:-)

Ah, ok;)

I've added a documentation bug:

https://github.com/oberstet/Autobahn/issues/90

to remind me ..

\Tobias

Clemens

unread,
Feb 20, 2012, 11:11:27 AM2/20/12
to Autobahn
Hi again,
I ran into a situation where I needed to do a dispatch of an event
from the server.
However, how do I make sure that I don't get the message I dispatch?
_dispatchEvent takes a list of excludes, but thats WampServerProtocol
instances, how do I get to my own instance of the protocol?

Thanks again,
Clemens



On Feb 8, 1:01 pm, Tobias Oberstein <tobias.oberst...@gmail.com>
wrote:

Clemens

unread,
Feb 20, 2012, 11:14:17 AM2/20/12
to Autobahn
I am sorry,
ignore that last message. I have a Client instance running too, I have
to figure out how to make sure that it doesn't get the event.

Tobias Oberstein

unread,
Feb 20, 2012, 11:19:20 AM2/20/12
to autob...@googlegroups.com, Clemens
nothing to figure out ..

http://www.tavendo.de/autobahn/doc/python/wamp.html#rpc-pubsub-clients

excludeMe (bool) – When True, don’t deliver the published event to myself (when I’m subscribed). Default: True.

Clemens

unread,
Feb 21, 2012, 2:13:28 AM2/21/12
to Autobahn
I have both a server and a client running in the same Python process.
The server posts the event, and then I have to make sure that its own
client doesn't react to it.
The only solution I could figure out was to append some extra
information to the event (alá a flag called 'fromserver)

On Feb 20, 5:19 pm, Tobias Oberstein <tobias.oberst...@gmail.com>
wrote:
> *excludeMe* (/bool/) -- When True, don't deliver the published event to

Tobias Oberstein

unread,
Feb 21, 2012, 2:42:47 AM2/21/12
to autob...@googlegroups.com, Clemens
Not sure if I get this .. you have

+ a server S and 1 client C1 running in same Twisted
+ that C1 client connects back to the server S (via loopback network)

??

Am 21.02.2012 08:13, schrieb Clemens:
> I have both a server and a client running in the same Python process.
> The server posts the event, and then I have to make sure that its own
> client doesn't react to it.
> The only solution I could figure out was to append some extra

> information to the event (al� a flag called 'fromserver)

Clemens

unread,
Feb 21, 2012, 3:51:25 AM2/21/12
to Autobahn
Sorry about being unclear!

I have a setup where the state of a number of javascript clients is
kept synchronized by Pub/Sub.
The python server has a number of responsibilities.
- It mediates events
- It provides RPC o the clients can load and reload their initial
state
- It persists all state changes to CouchDB
- It handles communication with external systems.

I have a client running on the same twisted as the server to listen
for events from the clients and call the appropriate methods on the
server.
When the server receives an event from an external system it
dispatches an event to update state on the clients.
However, I don't want the local client of the server to receive this
message because that can result in a loop of events.
To solve this I have appended information on the events I dispatch
whether they are from the server or not.

A more elegant solution would be to drop the client on the server, and
simply let the server listen for client events. But I don't see how
this is possible through the API?

I hope that cleared up the confusion? :-)

- Clemens

On Feb 21, 8:42 am, Tobias Oberstein <tobias.oberst...@gmail.com>
wrote:

Benjamin Bruheim

unread,
Feb 21, 2012, 3:57:15 AM2/21/12
to autob...@googlegroups.com
Hi

If I don't misunderstand:This is what I do to submit stuff only for the a single client:

self.dispatch(topicUri="http://example.com/topic/refresh", event=data, eligible=[self]) - from the ServerProtocol.

If you do this from a different protocol instance, getting a handle for it through the factory could work and pass that to eligible. Just remember to wrap it in a list first.

\\ Benjamin

Tobias Oberstein

unread,
Feb 21, 2012, 4:35:56 AM2/21/12
to autob...@googlegroups.com, Clemens
Am 21.02.2012 09:51, schrieb Clemens:
> When the server receives an event from an external system it
> dispatches an event to update state on the clients.
> However, I don't want the local client of the server to receive this
> message because that can result in a loop of events.

One option would be, within

WampServerProtocol.onSessionOpen

to check

self.peer.host

and if that is localhost (127.0.0.1) .. or whatever you want to filter, do

self.factory.excludeClients.append(self)

and then do your server-side dispatches

with "exclude = excludeClients"

This is essentially IP based filtering for server-side publication
(dispatch).

==

Another option would be to expose a RPC on server:

def excludeMeFromBlabla(self):
self.factory.excludeClients.append(self)

and call that at the very beginning in your "server-side client" and do the
dispatch as above.

This essentially allows clients to opt-out from server-side dispatches, but
still be subscribed to client-pubs on same topic.

==

Couple of other notes (don't know if that helps):

1)
The lists of exclude/eligible on dispatch() are server protocol instances.

As such, you need those Python object references, and hence it only makes
sense on server (where you have those refs).

2)
If you run a client C1 in _same_ twisted as the server, you can forward the
_client protocol instance_ of C1 to your server factory .. but that
won't be of use
for server dispatch filtering, since you need the _server_ protocol instance
**corresponding** to C1.

3)
However, for C1, both the

* client protocol instance
* server protocol instance

(which in your case exist in the same Twisted instance) hold

self.session_id

which is a unique, random _string_.

4)
.. and you can map

self.session_id to server protocol instance 2-way:

WampServerFactory.protoToSessions
WampServerFactory.sessionsToProto

WARNING: undocumented, still in flow ..

5)
Thus, a 3rd option is to forward the session_id of the client to be excluded
to server factory, map it to server proto instance, and add it to your
excludeClients list.

6)
With the session ID stuff, I didn't have the 5) use case in mind, but:

If you want to do publish from client _with_ exclude/eligible, Python object
references won't cut it.

Therefor, we've added a session ID .. which is a string .. in initial
welcome
message.

There is already some server code to do the mappings

session ID <=> server proto instance

but the client side (and WAMP protocol side) is not yet finished.

However, when it's done, you will be able to i.e. communicate a session ID
via any means, and then use that session IDs to do the exclude/eligible ..
and probably also do "direct client2client messaging" .. not only pubsub.

This was very terse .. when it's finished, I'll document it properly of
course;)


Clemens

unread,
Feb 21, 2012, 7:54:43 AM2/21/12
to Autobahn
Thank you for a thorough answer!
I'll try your suggestions out later this afternoon

- Clemens

On Feb 21, 10:35 am, Tobias Oberstein <tobias.oberst...@gmail.com>
wrote:
Reply all
Reply to author
Forward
0 new messages