Real Time Notifications

Implementing Pub/Sub for Notifications

Welcome! In this unit, we will delve into implementing Pub/Sub for notifications within our Redis-based backend system project. You've already learned how to manage user data, handle transactions, and use streams for event logging. Now, we'll add another powerful feature to our project: real-time notifications using Redis Pub/Sub (publish/subscribe). This will enable our system to send and receive messages instantaneously.

What You'll Build

In this unit, we'll focus on creating a simple real-time notification system using Redis Pub/Sub. Specifically, we'll cover:

  1. Publishing messages: how to send notifications.
  2. Subscribing to channels: how to receive and handle notifications.

We'll implement a complete C++ program using Boost.Redis that demonstrates both publishing and subscribing. The example will:

  • Create separate connections for subscribing and publishing, which is required for Pub/Sub.
  • Subscribe to a channel and listen for messages asynchronously.
  • Publish a JSON-formatted message to that channel.
  • Process and display the received message.

Messages will use JSON format for structured data:

JSON
{"user": "alice", "text": "Hello everyone!"}

Section 1: Includes, aliases, and the receive loop

This first section sets up the headers, type aliases, and the function that listens for Pub/Sub messages.

C++
#include <boost/redis.hpp>
#include <boost/redis/src.hpp>  // Include in one translation unit only
#include <boost/asio.hpp>
#include <nlohmann/json.hpp>

#include <iostream>
#include <memory>
#include <chrono>
#include <string>

namespace net   = boost::asio;
using boost::redis::connection;
using boost::redis::config;
using boost::redis::request;
using boost::redis::generic_response;
using boost::redis::logger;
using boost::system::error_code;
using json = nlohmann::json;

// Listen for Pub/Sub messages on the subscriber connection.
void start_receive_loop(std::shared_ptr<connection> sub_conn)
{
    auto resp = std::make_shared<generic_response>();

    // Set the response object for the next message.
    sub_conn->set_receive_response(*resp);

    // Receive the next push message asynchronously.
    sub_conn->async_receive(
        [sub_conn, resp](error_code ec, std::size_t) {
            if (ec) {
                if (ec == net::error::operation_aborted) {
                    // Connection was cancelled, just exit the loop.
                    return;
                }
                std::cerr << "Receive error: " << ec.message() << "\n";
                return;
            }

            // RESP3 push message structure for pub/sub:
            // ["push", "message", "<channel>", "<payload>"]
            auto const& nodes = resp->value();

            if (nodes.size() >= 4) {
                std::string kind = nodes[1].value;

                if (kind == "message") {
                    std::string channel = nodes[2].value;
                    std::string payload = nodes[3].value;

                    // Payload is JSON: {"user": "...", "text": "..."}
                    try {
                        json data = json::parse(payload);
                        std::string user = data["user"];
                        std::string text = data["text"];

                        std::cout << "Received message from " << user
                                  << " on channel '" << channel
                                  << "': " << text << "\n";
                    } catch (const std::exception& e) {
                        std::cerr << "JSON parse error: " << e.what() << "\n";
                    }

                    // Stop after receiving the first message.
                    sub_conn->cancel();
                    return;
                }
            }

            // For other push types (e.g., "subscribe" confirmations), keep listening.
            start_receive_loop(sub_conn);
        });
}

This section prepares the program to receive messages from Redis.

It includes the libraries for:

  • Redis communication with Boost.Redis
  • asynchronous execution with Boost.Asio
  • JSON parsing with nlohmann::json

It also defines start_receive_loop, which waits for push messages on the subscriber connection.

The function creates a generic_response object and gives it to the connection with set_receive_response(). Then it calls async_receive() to wait for the next Pub/Sub message without blocking the program.

For Pub/Sub in RESP3, a published message arrives in this structure:

text
["push", "message", "<channel>", "<payload>"]

The code checks that structure, extracts the channel and payload, parses the JSON message, and prints the sender and text. If the push message is something else, such as a subscribe confirmation, the function starts listening again.

This receive loop is what allows the subscriber connection to keep handling incoming messages asynchronously.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal