- 1. Chat Server with Mojo
- 2. Chat Server with Redis
- 3. Chat Server with PostgreSQL
- 4. Chat Server with PAGI
- 5. Chat Server with Thunderhorse
Start chat server as below:
$ perl chat-server.pl daemonI was introduced to JSON Virtual Frame and JSON Virtual Event when I shared the previous version of chat server.
To be honest, I had no clue about any of them, so I dig deep and found this:
This is a special way to structure data when you send a message through a WebSocket. Instead of manually converting a Perl data structure into a JSON string, you use the json key in the hash you pass to the send() method.
How it works?
When you call $tx->send({json => $data}), the build_message method in Mojo::Transaction::WebSocket intercepts this. It automatically calls Mojo::JSON::encode_json to serialise your Perl $data into a JSON text string. It then sends this string as a standard WebSocket text frame.
Why use it?
It makes your sending code cleaner and less error-prone by removing the manual encode_json step.
Example:
# Instead of doing this manually:
use Mojo::JSON 'encode_json';
$tx->send({text => encode_json({message => 'Hello', user => 123})});
# You can do this directly:
$tx->send({json => {message => 'Hello', user => 123}});This is a special event you can listen for on the WebSocket transaction. When a complete WebSocket message arrives, if the json event has any subscribers (i.e., you have an on(json => ...) callback), the transaction will automatically attempt to decode the message payload from JSON.
How it works?
When a message is fully assembled (in parse_message), it checks if there are any subscribers for the json event. If there are, it passes the raw message through Mojo::JSON::j() (which is context-aware and decodes JSON). The decoded Perl data structure is then emitted to your callback.
Note: This event is only emitted if you are listening for it. This ensures you don't incur the performance cost of decoding JSON if you don't need it. It can decode both text and binary messages, as long as they contain valid JSON.
Example:
$tx->on(json => sub {
my ($tx, $received_data) = @_;
say "Received message: " . $received_data->{message};
});I have applied both in this version.
For this version of chat server, we need Redis running locally. Luckily I had docker container running Valkey.
If you don't know Valkey, then I suggest you take a look at this blog post of mine.
First start the Valkey container as below:
$ docker start valkeyTesting time, make sure it reachable on default port as below:
$ redis-cli -h 127.0.0.1 -p 6379 pingYou should see PONG in response to the above command.
Time to start the chat server listening to port 3000 like below:
$ perl chat-server-v2.pl daemon -l http://*:3000Let's start another chat server this time listening to port 3001 like below:
$ perl chat-server-v2.pl daemon -l http://*:3001Now open browsers to both ports, users on different ports can now chat to each other.
Well, we need PostgreSQL database now and I am not willing to setup database from scratch.
The easy option is to create a docker container running the PostgreSQL database.
So here is the docker compose configuration file: docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: chat_postgres
restart: unless-stopped
environment:
POSTGRES_USER: chatuser
POSTGRES_PASSWORD: chatpass
POSTGRES_DB: chat_db
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U chatuser -d chat_db"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:Start the container first as below:
$ docker-compose up -dCheck the status of the container as below:
$ docker-compose psTest the connection now as below:
$ docker exec -it chat_postgres psql -U chatuser -d chat_db
psql (16.11)
Type "help" for help.
chat_db=# LISTEN chat_messages;
LISTEN
chat_db=# NOTIFY chat_messages, '{"type":"test"}';
NOTIFY
Asynchronous notification "chat_messages" with payload "{"type":"test"}" received from server process with PID 2333.
chat_db=# \qWe have PostgreSQL up and running.
Let's start two chat servers listening to port 3000 and 3001.
$ perl chat-server-v3.pl daemon -l http://*:3000$ perl chat-server-v3.pl daemon -l http://*:3001Once again, open browsers to both ports, users on different ports can now chat to each other.
Start chat server as below:
$ perl chat-server-v4.plTo run this server, we need Perl v5.40 as enforced by Thunderhorse,
You will also need PAGI.
$ pagi-server --port 3000 chat-server-v5.pl