http://www.perlmonks.org?node_id=1092726

farkhadbek has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I would like to have chat-application, very similar to this websocket example:

https://github.com/kraih/mojo/wiki/Writing-websocket-chat-using-Mojolicious-Lite

#!/usr/bin/env perl use utf8; use Mojolicious::Lite; use DateTime; use Mojo::JSON; get '/' => 'index'; my $clients = {}; websocket '/echo' => sub { my $self = shift; app->log->debug(sprintf 'Client connected: %s', $self->tx); my $id = sprintf "%s", $self->tx; $clients->{$id} = $self->tx; $self->receive_message( sub { my ($self, $msg) = @_; my $json = Mojo::JSON->new; my $dt = DateTime->now( time_zone => 'Asia/Tokyo'); for (keys %$clients) { $clients->{$_}->send_message( $json->encode({ hms => $dt->hms, text => $msg, }) ); } } ); $self->finished( sub { app->log->debug('Client disconnected'); delete $clients->{$id}; } ); };

But this is not working when this aplication is running under hypnotad.

Problem is in 'global' varibale $clients where reference to client's tx is stored. $clients is not global enough and of course not shared between hypnotoad workers (processes).

To solve this problem, the author of this example offered use Database or KeyValueStore to save "reference to clients tx".

Can anybody suggest how to do it?

Any idea

Thanks

Replies are listed 'Best First'.
Re: Mojolicious: Websocket example under hypnotoad
by Anonymous Monk on Jul 09, 2014 at 01:13 UTC

    I don't understand what the problem is that you're trying to solve :)

    But this is not working when this aplication is running under hypnotad.

    In what way?

    Problem is in 'global' varibale $clients where reference to client's tx is stored. $clients is not global enough and of course not shared between hypnotoad workers (processes).

    Why is this a problem (what is the problem)? What are you trying to solve?

    To solve this problem, the author of this example offered use Database or KeyValueStore to save "reference to clients tx". Can anybody suggest how to do it?it?

    Where did the author of mojo offer this wisdom, do you have a link? http://irclog.perlgeek.de/mojo/...?

    I might be able to help if I could understand what trouble you're experiencing

      This is an example of chat application.

      This is Mojolicious application and it can be run in different ways.

      One of the ways to run under the hypnotoad.

      What is Hypnotoad?

      Hypnotoad is a full featured, UNIX optimized, preforking non-blocking I / O HTTP and WebSocket server, built around the very well tested and reliable Mojo :: Server :: Prefork, with IPv6, TLS, Comet (long polling), keep-alive, connection pooling, timeout, cookie, multipart, multiple event loop and hot deployment support that just works.

      Hypnotoad is PREFORKING server therefore has many processes (workers). Variable $clients contains references to objects Mojo::Transaction. This object is required for calling send_message (send a message to the chat user)

      The problem is that this variable is not shared between workers. And connected users in the one worker are not visible in another.

      I'm trying to solve this problem.

      If you are after these explanations did not understand anything then do not worry about it

      Thanks

        I think you will have to add much more session handling and inter-server communication for making the "real-time" aspect of WebSockets work across multiple processes. Personally, I would try really hard to avoid this, for example by moving the non-WebSocket parts of your application to a different machine/CPU/process.

        If you still want to go the route of keeping one WebSocket open to each client but have communication between them, you will need to have communication between your server processes too, to move the information from the other parts of your application to the process that holds the socket of the client. I think this is where message brokers like ZeroMQ come in to make IPC easier. Other people use HTTP as their internal transport ("SOA" respectively "microservices").

        I think that the multi-process approach massively complicates your infrastructure and setup, but if there is no easy way to handle this within one process then you'll have to look in that direction.

        The problem is that this variable is not shared between workers. And connected users in the one worker are not visible in another.

        Ok, thats, easy, use a database for IPC, store all the messages in a database, then all the workers can get messages from this database (or service)

        Then it doesn't matter which worker the clients are connected to, they all get messages from database

        This is arranged by poling, see https://metacpan.org/pod/Mojolicious::Guides::Cookbook#Timers

        So something like this (maybe with more smaller events ->on and what not)

        use Mojo::IOLoop; my $clients = {}; my $lasttime = time; Mojo::IOLoop->recurring( 1 => sub { if ( my( $newMessages, $time ) = GetNewFromDb($lasttime) ) { $lasttime = $time; for my $msg (@$newMessages) { MsgToClients( $msg, $clients ); } } }, ); websocket '/echo' => sub { ... $self->receive_message( sub { my ($self, $msg) = @_; MsgToDb( $self, $msg ); ## NO ## MsgToClients( $self, $msg , $clients ); ## NO }, ); ... }; sub GetNewFromDb { my( $lasttime ) = @_; ... my $time = eval { $last_message->{time} }; return $messages, $time; }

        instead of $lasttime being time based, might be better to use last_insert_id of some sort from the database or some such .... details :)

        update: found the converstation

        http://irclog.perlgeek.de/mojo/2013-06-26#i_7255079

        20:15 ghandi Hi There! I've been playing around with the Mojo +WebSocket Chat-Example from https://github.com/kraih/mojo/wiki/Writi& +#8203;ng-websocket-chat-using-Mojolicious-Lite 20:15 And now i'm wondering: Do i realy need to keep track of +the clients myself in %clients? Wouldn't it be cooler to just use a c +ustom event that i emit on a new incoming message? 20:16 gtodd hmm 20:17 define "cooler" :-) 20:17 Anyway maybe you could answer questions about Mojo on St +ackoverflow and become famous :) 20:17 there's one up there about websockets ... 20:17 for me cooler would be "faster" 20:18 ghandi cooler like more slick 'n sexy. Like let the Even +t-System take care of the Client-Management since it already knows ab +out the connections 20:18 gtodd POE could be sexy but Mojo-POE I don't know 20:20 sri ghandi: no, in fact, that example is not very good t +o begin with 20:20 doesn't scale past one process 20:20 gtodd ghandi: what you just described seems too easy or +obvious 20:21 oops hehe I agree with sri 20:21 sri it's a minimal example, anything serious needs a bac +kend like redis pub/sub 20:21 gtodd ghandi: btw. I meant too obvious or easy because i +t makes sense to me and I am usually wrong :-) 20:22 ghandi so in my case: I'm thinking about some live-intar +action page, where multiple people whatch the page while a single per +son sends in some updates. For this i need a dedicated "collaboration +"-Server to which mojo talks? 20:26 btyler like sri said, redis with pub/sub is precisely th +at 'middle person' that multiple mojo processes can talk to 20:27 ghandi But don't i loose then the event-driven part of t +he Websockets? The client then has to poll the app which then will lo +okup on redis? 20:28 sri i think Mojo::Redis is actually battle tested now wi +th wirc using pub/sub too 20:30 batman: Mojo::JSON uses render_json in the example 20:30 Drossel joined #mojo 20:31 bek ghandi: /quit 20:33 ghandi sri: Mojo::Redis looks interesting. So just to ge +t something right: The Mojo-Event-System is Process-Based, right? Whi +ch means: Client A on $PID 1 can't talk to Client B on $PID 2? 20:34 sri that's how everything in perl works 20:35 ghandi Ok, thanks ;)

        So there is the context, use Mojo::Redis - Asynchronous Redis client for Mojolicious. somehow

        Redis - Perl binding for Redis database

        Redis is an open-source, networked, in-memory, key-value data store with optional durability. It is written in ANSI C. The name Redis means REmote DIctionary Server The Publish/Subscribe feature is fully implemented, so a client of a slave may SUBSCRIBE to a channel and receive a full feed of messages PUBLISHed to the master,