Say Hello to Mercure 0.10!

I’m very excited to announce the immediate availability of the version 6 of the Mercure Internet Draft as well as of the version 0.10 of the reference implementation!

Mercure is a real-time protocol built on top of Server-sent Events and leveraging HTTP/2+. It allows to push messages to JavaScript webapps, mobile apps or IoT devices with ease. Client-side, it relies on the native EventSource class and doesn’t require any JS dependency. Server-side, it allows to use a Hub to handle persistent connections and dispatch messages sent, for instance, from your REST or GraphQL APIs. An open source Hub designed for performance and high loads is developed in parallel to the specification. Get started with Mercure.

Since the initial release of Mercure, we gather feedback from the ever-growing community of users and contributors. This allowed us to identify the pain points, some limitations and new use cases. It took months of work to get there, but these new versions of the protocol and of the FOSS hub (written in Go) fix all the limitations and design issues we identified so far. They are by far the best and the fastest versions ever released! Let’s discover these changes and new features.

Protocol Changes

Authorization Mechanism

One of the key features of Mercure is its authorization mechanism. It allows to send private updates, which will be received only by subscribers having the appropriate credentials.

Until the last version of the protocol, this mechanism was using the concept of targets, and was hard to understand for newcomers.
In the 6th version of the protocol, the authorization mechanism has been dramatically simplified, while staying as powerful as before.

The authorization mechanism has been totally revamped and the concept of targets is gone. Now, to send a private update, the publisher must only mark it as private when sending the POST request to the hub using the new private parameter.

On the other hand, to receive a private update, a subscriber must present a JWT to the hub containing a list of topic selectors in the mercure.subscribe claim of the token.

A private update will be received by a subscriber only if:

  • the topic of the update is contained in the list of topic selectors
  • the topic matches an URI template present in the list
  • the topic selectors list contains the special value *, which allows to receive all updates.

More advanced use cases such as cherry-picking which updates will be received by a subscriber are also supported thanks to existing features such as alternate topics.

Also, the new concept of target selectors can also be used to subscribe to (public or private) updates, making the whole protocol more consistent.

Learn more about topic selectors and the authorization mechanism.

Presence API

One of the most requested feature was the ability to retrieve the list of currently connected subscribers. This is now possible thanks to a new web API exposed by the hub! It’s also possible for a subscriber to receive Mercure updates when other subscribers connect or disconnect.

Hubs can now expose endpoints returning the list of connected users as JSON-LD documents:

  • /.well-known/subscriptions returns the list of all active subscriptions
  • /.well-known/subscriptions/{topic} returns the list of active subscriptions for a specific topic
  • /.well-known/subscriptions/{topic}/{subscriber} returns details for a single subscription

Also, after having fetched the initial list of connected users, clients can subscribe (using the standard subscription mechanism) to connection status updates by using these URIs as topics.

To access to this presence API, subscribers must be authenticated and have topic selectors matched by these URIs.

Finally, arbitrary data (such as the subscriber’s username or IP address) can be attached to a subscription and retrieved by other subscribers using the new mercure.payload JWT claim.

The debug UI shipped with the reference hub now supports this new feature:

Learn more about active Mercure subscriptions.

Event Sourcing

The native features of Mercure, including its built-in history and its state reconciliation mechanism make it a convenient solution to implement Event Sourcing.

The latest version of the protocol introduces changes making dedicated to this kind of usages.

First, it is now possible to set the value of the Last-Event-ID HTTP header (or of the query parameter with the same name) to earliest to retrieve the all past events. This allows to use the Mercure hub similarly to Apache Kafka, but directly from the browser (or any other kind of HTTP client).

Additionally, when a Last-Event-ID header or parameter is set, the hub will now always return a Last-Event-ID header in the HTTP response. If it matches the requested one, it means that all requested events have been returned, otherwise, it means that the requested event may have been deleted by the hub (if it stores a limited number of events for instance), or that this event ID doesn’t exist.

Learn more about Event Sourcing and state reconciliation capabilities of Mercure.

IRIs, Strings, and Fragment Identifiers

The protocol has been made more consistent by allowing to use IRIs (recommended) or plain strings (discouraged but sometimes practical) for every identifiers: topics, event IDs, subscriber IDs…

Event IDs starting by the # character (URI fragment) are now reserved for generation by the hub. This allows a hub to use offsets generated by systems such as Apache Kafka, Apache Pulsar or Redis Streams as update identifiers while keeping these identifiers globally unique (because they are relative to the Hub URI).

Changes in the Reference Implementation

All the features described previously have already been implemented in the reference implementation (the open source hub written in Go). But that’s not all! The hub has also been dramatically improved during the past few months.

New Engine

Until version 0.10, the reference hub was using an engine working like this: when a new update was posted by a publisher, a new light-weight process (goroutine) was started and the update was pushed to every active subscriber in a loop. If a Last-Event-ID header (or parameter) was passed by a subscriber to request past events, these past events were retrieved from the persistence layer and dispatched from another goroutine.

However, this approach had several drawbacks:

  • If a subscriber was slow to receive an update, it was slowing down the main loop, and so increasing the latency also for other subscribers.
  • If a Last-Event-ID was passed, new events could be received before events coming from the history (for example, when a new event was sent from the main goroutine while another goroutine was still retrieving past events from the persistence layer). The order in which events were received wasn’t 100% guaranteed.

Mercure 0.10 contains a brand new engine that fixes these issues! This new engine leverages thoroughly the iconic features of the Go programming language: channels and goroutines. Now, two buffers are assigned to each subscribers: the first one stores events coming from the history while the second one stores “live” events. When a new event is published, the main goroutine pushes this event in the “live” buffers without waiting for events to be dispatched. Then a dedicated goroutine per subscriber pops events from the buffers associated with it. So every subscriber can receive the events as its own pace, without impacting the rest of the system.

This buffer system also fixes the order issue: goroutines dedicated to specific subscribers wait for all updates coming from the history to be dispatched before starting to dispatch updates stored in the “live” buffer.

Last but not least, this new system allows to disconnect subscribers that are too slow to receive the updates. A new config option, dispatch_timeout, has been introduced to configure the maximum time that a subscriber can take to receive an update before being disconnected to free resources.

If you’re interested in the internals, take a look at this piece of Go code!

A big thanks to Dani Kaplan for his precious help testing and debugging this new engine under high loads!

Prometheus and Health Checks Support

Prometheus is a very popular monitoring system and time series database for Cloud Native applications. Jérémy Decool made a series of great contributions that add support for Prometheus to the Mercure hub. Metrics include (among other things) the number of currently connected subscribers, the number of dispatched updates, the number of served HTTP request segregated by status code, the memory usage, the current number of processes and the number of open file descriptors!

In addition, a new URL (/healthz) is now available to check if the hub is up and running without polluting the HTTP logs with useless entries. This particularly useful for Kubernetes’ liveness and readiness probes.

Version Flag

Jérémy Decool also contributed a new --version flag allowing to know what exact version of the Hub is being used. It looks obvious and priceless, but it was a missing feature. Thanks Jérémy for adding it!

Examples and Demos

We updated the documentation as well as all existing examples (in various programming languages) to be in sync with the latest version of the spec. I also added examples and demos illustrating new features, including a chat written in JavaScript and Python:

This demonstration chat is now available online, try it!

Community

Mercure is becoming more and more popular! It recently reached 2k stars on GitHub and integrations with existing ecosystems are now plenty. For this new version, we worked with the community to keep libraries and other Mercure-related projects in sync with the latest changes in the spec. Most of them are already compatible including (but not limited to):

For some others (for instance, the Java library and the Python library), Pull Requests are open. Finally, some tools still need to be updated including the awesome alternative Hub written in Node created by Nicolas Coutin and the Yii framework integration. Any help is very welcome!

To keep up with Mercure news, you can now follow our brand new Twitter account.

Finally, I’m very happy to announce that Les-Tilleuls.coop now provides official training for Mercure, in English and in French!

Improving Libraries Used by Mercure

One of the most powerful feature of Mercure is the ability to use URI templates to subscribe to topics matching patterns. Under the hood, the reference implementation of Mercure use the uritemplate Go library.

The performance of the Mercure hub highly depends of the performance of this library. So I worked with Kohei Yoshida (the author of this lib) to improve its performance, add benchmarks and fix some bugs! This also dramatically improved the performance of the hub (and of other projects using this library). Thank you Kohei for your quick reviews and releases!

The development of the new version of the hub also allowed me to find an issue in the Go standard library, and to open a Pull Request to fix it.

Hosted and High Availability Versions

Managed and On Premise High Availability (cluster) versions of the Mercure hub are available for a while now. They are already compatible with Mercure 0.10 and are now out of beta!

I want to thank very much our first customers, who trusted us, helped us to battle-test our hosting platform and gave us incomparable feedback allowing us to improve both the open protocol, the Managed and On Premise versions and the Open Source hub! They also allow us to invest more in the protocol’s development and in the Open Source version! So thank you again.

I also want to thank very much my GitHub sponsors, who allow me to work always more on my free software projects, including Mercure but also Vulcain, API Platform and the Symfony components I maintain.

Try and Star Mercure!

If you aren’t using Mercure yet, you can install a hub in a few seconds, or use the online demo, give it a try! If you like the project, show your support by starring it on GitHub!

HTTP/2: speed up your apps and dispatch real time updates (Symfony and API Platform’s features announcement)

HTTP/2 can improve the loading time of webpages up to 2 times. Did you know that it’s very easy to optimize your Symfony applications to leverage the advanced features of this new protocol?

This talk also contains the announcement of 3 new PHP packages:

Agenda:

  • a historically contextualized presentation of the different versions of the HTTP protocol;
  • a state of the protocol support in the PHP ecosystem;
  • a guide explaining how to serve your PHP and Symfony apps with h2, using Nginx, Apache and Docker;
  • many code samples showing how to use h2 to improve the loading time of your assets and APIs using the WebLink component and Twig
  • examples of how to take advantage of the protocol using the curl and Guzzle clients
  • the Mercure Protocol
  • Mercure x Symfony
  • Mercure x API Platform

HTTP/2, PHP and Symfony: a brief history of the protocol powering the web, and how to use it

Here are the slides I presented yesterday during the Paris Symfony Meetup.

On the menu: a brief history of HTTP, how to use it with PHP (server and client-side) and how to push with Symfony 4.

Learn more about Symfony’s HTTP/2 support in my in-depth presentation of the WebLink Component.

Symfony 4: HTTP/2 Push and Preloading

A few months ago, I’ve contributed a new component to Symfony: WebLink. By implementing cutting edge web standards, namely HTTP/2 Server Push and W3C’s Resource Hints, it brings great opportunities to boost webapp’s performance.

Thanks to Symfony WebLink, HTTP/2 (h2) servers are able to push resources to clients before they even know that they need them (think to CSS or JavaScript files, or relations of an API resource). WebLink also enables other very efficient optimisations that work with HTTP 1:

  • telling the browser to fetch or to render another webpage in the background ;
  • init early DNS lookups, TCP handshakes or TLS negotiations

Let’s discover how easy it is to use it and the real life benefits you can expect. The WebLink components is available since Symfony 3.3 (it means you can start to use it today), but in this article I’ll use Symfony 4 to celebrate the release of its first beta yesterday!

To get started, download my Docker installer and runtime for Symfony 4/Flex. It includes everything you need to run Symfony (PHP 7.1 configured properly for Symfony,  and Composer) and a development reverse proxy (Apache) supporting HTTP/2 Server Push and HTTPS (most clients only support HTTP/2 over TLS).

Unzip the downloaded archive, open a shell in the resulting directory and run the following commands:

# Optional: use Symfony 4 beta (will default to Symfony 3.3 if you omit this line)
$ docker-compose build --build-arg STABILITY=beta app
# Install Flex and start the project
$ docker-compose up

Open https://localhost, if this nice page appears, you successfully created your first Symfony 4 project and are browsing it in HTTP/2!Let’s create a very simple homepage using the Twig templating engine. Because Symfony Flex starts as a micro framework, the first step is to install the library itself: docker-compose exec app composer req twig (run this in a new shell)

Flex is smart enough to download Twig, automatically register it into Symfony and enable Symfony features requiring the library. It also generates a base HTML5 layout in the templates/ directory (we will not use it but you should).

Now, download Bootstrap 4, extract the archive and copy the file dist/css/bootstrap.min.css in the public/ directory of our project. As you may know Symfony already has a nice integration with the upcoming version 4 of the most popular CSS framework.

Note: in a real project, you should use Yarn or NPM with Symfony Encore to install Bootstrap.

Now, it’s time to create the template of our homepage:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Welcome!</title>
    <link rel="stylesheet" href="/bootstrap.min.css">
</head>
<body>
    <main role="main" class="container">
        <h1>Hello World</h1>
        <p class="lead">That's a lot of highly dynamic content, right?</p>
    </main>
</body>
</html>

And finally, register our new template as the homepage using the builtin TemplateController:

# config/routes.yaml
index:
    path: /
    defaults:
      _controller: 'Symfony\Bundle\FrameworkBundle\Controller\TemplateController::templateAction'
      template: 'homepage.html.twig'

Clear the cache: docker-compose exec app bin/console c:c (this step will become optional when #24642 will be merged)

Refresh your browser, this nice HP should appear:

2 HTTP requests are issued by the browser, one for the homepage, and another one for Bootstrap.

But we know from the very beginning that the browser will need Bootstrap. Instead of waiting that the browser downloads the homepage, parses the HTML (notice “Initiator: Parser” in Chrome DevTools), encounters the reference to bootstrap.min.css and finally sends a new HTTP request, we could take benefit of the HTTP/2 Push feature to directly send both resources to the browser. Let’s do it!

Install the WebLink component: docker-compose exec app composer req weblink

As for Twig, Flex will automatically download and register this component into our app.

Now, update the template to use the preload Twig helper that leverages the WebLink component:

<!-- ... -->
    <link rel="stylesheet" href="{{ preload('/bootstrap.min.css') }}">
<!-- ... -->

Reload the page:As you can see (Initiator: Push), both responses have been sent directly by the server. bootstrap.min.css has started to be received before the browser even requested it!

How does it works?

The WebLink component tracks Link HTTP headers to add to the response. When using the preload() helper, a Link header with a preload rel attribute is added to the response:

According to the Preload specification, when a HTTP/2 server detects that the original (HTTP 1) response contains this HTTP header, it will automatically trigger a push for the related file in the same HTTP/2 connection.

The Apache server provided by my Docker setup supports this feature. It’s why Bootstrap is pushed to the client! Popular proxy services and CDN including Cloudflare, Fastly and Akamai also leverage this feature. It means that you can push resources to clients and improve performance of your apps in production right now! All you need is Symfony 3.3+ and a compatible web server (the free version of Nginx doesn’t support Server Push) or CDN service.

If you want to prevent the push but let the browser preload the resource by issuing an early separate HTTP request, use the nopush attribute:

<!-- ... -->
    <link rel="stylesheet" href="{{ preload('/bootstrap.min.css', {nopush: true}) }}">
<!-- ... -->

Before using HTTP/2 Push, be sure to read this great article about known issues, cache implications and the state of the support in popular browsers.

In addition to HTTP/2 Push and preloading, the WebLink component also provide some helpers to send Resource Hints to clients, the following helpers are available:

  • dns_prefetch: “indicate an origin that will be used to fetch required resources, and that the user agent should resolve as early as possible”
  • preconnect: “indicate an origin that will be used to fetch required resources. Initiating an early connection, which includes the DNS lookup, TCP handshake, and optional TLS negotiation, allows the user agent to mask the high latency costs of establishing a connection”
  • prefetch: “identify a resource that might be required by the next navigation, and that the user agent should fetch, such that the user agent can deliver a faster response once the resource is requested in the future”
  • prerender: “identify a resource that might be required by the next navigation, and that the user agent should fetch and execute, such that the user agent can deliver a faster response once the resource is requested in the future”

The component can also be used to send HTTP link not related to performance. For instance, any link defined in the HTML specification:

<!-- ... -->
    <link rel="alternate" href="{{ link('/index.jsonld', 'alternate') }}">
    <link rel="stylesheet" href="{{ preload('/bootstrap.min.css', {nopush: true}) }}">
<!-- ... -->

The previous snippet will result in this HTTP header being sent to the client:

Link: </index.jsonld>; rel="alternate",</bootstrap.min.css>; rel="preload"; nopush

 

You can also add links to the HTTP response directly from a controller or any service:

<?php
// src/Controller/BlogPostAction.php
namespace App\Controller;

use Fig\Link\GenericLinkProvider;
use Fig\Link\Link;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class BlogPostAction
{
    public function __invoke(Request $request): Response
    {
        $linkProvider = $request->attributes->get('_links', new GenericLinkProvider());
        $request->attributes->set('_links', $linkProvider->withLink(new Link('preload', '/bootstrap.min.css')));

        return new Response('Hello');
    }
}

# app/config/routes.yaml
blog_post:
    path: /post
    defaults:
      _controller: 'App\Controller\BlogPostAction'

Last but not least, as all Symfony components, WebLink can be used as a standalone PHP library:

<?php

require __DIR__.'/../vendor/autoload.php';

use Fig\Link\GenericLinkProvider;
use Fig\Link\Link;
use Symfony\Component\WebLink\HttpHeaderSerializer;

$linkProvider = (new GenericLinkProvider())
    ->withLink(new Link('preload', '/bootstrap.min.css'));

header('Link: '.(new HttpHeaderSerializer())->serialize($linkProvider->getLinks()));

echo 'Hello';

It is already as a standalone library by the Bolt CMS. While we’re speaking about interoperability, WebLink can deal with any link implementing PSR-13.

Thanks to Symfony WebLink, there is no excuses to not to switch to HTTP/2!

Learn more about these new features in the relevant Pull Requets: #22273 and #21478.

Your Symfony app is slow? Contact Les-Tilleuls.coop, we can help you make it fast!