How to install Mercure on Debian

Introduction

Mercure is an open source tool built on top of HTTP and SSE which allows you to easily implement true real-time communication in your web applications. While this concept is not unique or new, Mercure lets you implement it fairly easily. I’m using the term “true real-time communication” because developers frequently simulate real time communication by polling. Polling works great for small scale applications, but for large scale web apps real-time communication can save you a whole bunch of useless requests thus traffic and CPU load. But what about the WebSocket API and isn’t it exactly the same? Well, it is and it isn’t. The WebSocket API is ‘low level’, whereas Mercure is ‘high level’. Using Mercure you don’t have to manually implement authorization, re-connection, presence API, etc.

In a nutshell, if you need a persistent connection between a client and a server, which can be used by both to send and receive data, and you don’t want to implement it all from scratch using WebSockets – Mercure is the tool for the job.

In this guide, you will learn how to:

You can download all of the example files for this guide here

How to install Mercure

First things first, head to the Releases page in the Mercure GitHub. In this guide I’ll be using the v0.10.2 for Linux x86/64 (mercure_0.10.2_Linux_x86_64.tar.gz).

wget https://github.com/dunglas/mercure/releases/download/v0.10.2/mercure_0.10.2_Linux_x86_64.tar.gz && mkdir mercure && tar -zxvf mercure_0.10.2_Linux_x86_64.tar.gz -C mercure

Now, the Mercure executable is extracted and you can enter the directory and run it:

cd mercure && ./mercure

Which should output:

Invalid config: one of “jwt_key” or “publisher_jwt_key” configuration parameter must be defined.

But what is JWT? JWT stands for JSON Web Token and is an open standard for securely transmitting information between parties in JSON. I won’t go into too much detail about JWT here, so if you’re interested head to their homepage to learn more.
The JWT Key is a string used to encode the JWT Token. Think of it as a password.

Let’s supply a jwt-key and rerun mercure:

./mercure --jwt-key=’!ChangeMe!’ --addr=’localhost:3000’ --allow-anonymous --cors-allowed-origins=’*’

Now everything should be running and you should be able to open the “http://127.0.0.1:3000” address in your browser and see:

Welcome to Mercure!

How to configure Mercure as a Service

So far so good, but there is one problem: if you terminate your terminal or the command gets closed / fails your mercure server will shut down and your customers will lose their persistent connections and live updates.
In order to avoid accidental shut downs of our Mercure Hub I will use a software called Supervisor or Supervisor daemon (Supervisord). In a few words, Supervisord is a process management system that will keep our process running, reboot on crashes and enable logging.

Installing supervisor

Using the root user, run the following command to install the Supervisor package:

apt-get install supervisor


You can ensure the supervisor is running with the following command:

systemctl status supervisor

Setting up supervisor for Mercure

The configuration files for all programs supervisor is managing can be found in /etc/supervisor/conf.d In order to add mercure we’ll create a new ‘mercure.conf’ file there:

#/etc/supervisor/conf.d/mercure.conf
[program:mercure]
command=/path/to/mercure
process_name=%(program_name)s_%(process_num)s
numprocs=1
environment=JWT_KEY="!ChangeMe!",ADDR=':3000', DEMO=1, ALLOW_ANONYMOUS=1, CORS_ALLOWED_ORIGINS=, PUBLISH_ALLOWED_ORIGINS='', USE_FORWARDED_HEADERS=1, DEBUG=1
directory=/tmp
autostart=true
autorestart=true
startsecs=5
startretries=10
user=www-data
redirect_stderr=false
stdout_capture_maxbytes=1MB
stderr_capture_maxbytes=1MB
stdout_logfile=/path/to/mercure/out.log
stderr_logfile=/path/to/mercure/error.log

Important: it is crucial to change the path in the file to the path of mercure in your system, and the JWT_KEY to the key you’ll be using. This configures Supervisor to provide the needed environment variables which Mercure uses for options, you can read more about this concept in the config docs of Mercure here. Most importantly, our script will autostart, autorestart and log to the /path/to/mercure/out.log and /path/to/mercure/error.log files.

Now we have to make supervisor aware of our new config file by running the following command:

supervisorctl reread

The output to which should be:

mercure: available

Now in order to run the loaded config file we run:

supervisorctl update

And to make sure everything is running correctly you should run:

supervisorctl status

To which the output should be similar to:

Mercure:mercure_0 RUNNING pid XXX, uptime 0:01:00

Now that we’ve installed mercure and made sure the server will autostart and autoreload on crashes, we can test the whole setup with a simple example.

Using Mercure in an example

Generate a JWT Token

To push updates to a Mercure Hub we need a Publisher, but we have to authorize it with a JWT Token first. The anatomy of a JWT Token mainly consists of a secret key and a payload. The secret key we’ve already set in the “How to install mercure” chapter. In order to authorize the Publisher to push to all topics our payload should contain at least the following structure:

{
  "mercure": {
    "publish": [
      "*"
    ]
  }
}

We can easily generate a token with this payload in the JWT.io website, changing the JWT secret key with the one we configured above. In this case “!ChangeMe!”

And the configured JWT Token is:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.obDjwCgqtPuIvwBlTxUEmibbBf0zypKCNzNKP7Op2UM

Sending an update to a Mercure topic using PHP

The easiest way I’ve found to push updates to Mercure using PHP is by using the open-source symfony/mercure package:

composer require symfony/mercure

To push an update to the Mercure Hub you can use the package in the following way:

<?php
//Pusher.php
require_once("vendor/autoload.php");
 
// change these values accordingly to your hub installation
define('HUB_URL', 'http://127.0.0.1:3000/.well-known/mercure');
define('JWT', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.obDjwCgqtPuIvwBlTxUEmibbBf0zypKCNzNKP7Op2UM');
 
use Symfony\Component\Mercure\Jwt\StaticJwtProvider;
use Symfony\Component\Mercure\Publisher;
use Symfony\Component\Mercure\Update;
 
$publisher = new Publisher(HUB_URL, new StaticJwtProvider(JWT));
// Serialize the update, and dispatch it to the hub, that will broadcast it to the clients
$id = $publisher(new Update('https://example.com/codingstories/1.jsonld', 'Hi from CodingStories.net!'));

The idea of the pusher is to tell the Mercure Hub to push an update to all clients subscribed to “https://example.com/codingstories/1.jsonld” with the payload “Hi from CodingStories.net!”

Subscribing to a topic in Mercure using Javascript

Now in order to properly test our system we need a client. Lets create an index.html file with the following contents:

<script>
   const url = new URL('http://localhost:3000/.well-known/mercure');
   url.searchParams.append('topic', 'https://example.com/codingstories/{id}');
   // The URL class is a convenient way to generate URLs such as http://localhost:3000/.well-known/mercure?topic=https://example.com/books/{id}
 
   const eventSource = new EventSource(url);
 
   // The callback will be called every time an update is published
   eventSource.onmessage = e => alert(e.data); // do something with the payload
</script>

This subscribes our browser to the topic https://example.com/books/{id} where {id} is a parameter. Now test it out by openning index.html and running the Pusher.php by:

php Pusher.php

Your browser should have opened a JavaScript alert instantly.

Success

Now test opening the index.html a few more times in separate tabs and run the Pusher again. The tabs in this case are somewhat similar to multiple clients and running the pusher sends real-time updates to all tabs.

You have now sucessfully setup a Mercure hub, a Pusher and a Client and are on your way of making your reactive apps work.

About Pavel Petrov 2 Articles |  19 How-tos
Pavel is a senior developer for the last 7 years, with extended interest in Linux administration, WordPress and Symfony.

Be the first to comment

Leave a Reply

Your email address will not be published.


*