redis pubsub spring boot

Overview:

In this article, I would like to show Redis PubSub with Spring Boot which can be used to broadcast messages across multiple services in a Microservices architecture. I assume you have basic knowledge on Redis + Spring Boot integration. If you are new to Spring Boot Redis, check the below article.

  • Spring WebFlux Redis Integration

Redis PubSub:

PubSub is an asynchronous messaging model for service-to-service communication in Microservices architecture. That is, a service ( Publisher ) instead of sending a message to a specific recipient, it publishes the message to a Topic/Channel , through which interested parties ( S ubscribers ) receive the message.

redis pubsub with spring boot

Advantages
One-To-Many Communication A Publisher can publish a single message where N number of subscribers can receive and react to the message.
Loose Coupling Services are not tightly coupled. Any service can consume / ignore the message
Better Performance Publisher does not have to call N number of services. Instead it just publishes a message into a topic.
It does not have to have any knowledge on the subscribers. It is not blocked.

The PubSub model also has some limitations.

Limitations
PubSub is fire-forget model. If the receivers are offline, they might not receive the message.
(Take a look at Redis Stream for this use case)
PubSub is fan-out model. That is, multiple instances of the same service will receive the message.
(Take a look at Redis Stream for this use case)

Sample Application:

We are going to create a 2 simple Spring Boot applications. 1 will be acting like a publisher and other one will be a subscriber .

  • Publisher: this application will periodically publish Jokes
  • Subscriber: There could be N number of subscribers. In our case we will have 1 subscriber.
    • This subscriber will be notified as and when a new joke is published.
    • Subscriber could do anything with the joke. In our case we will just print on the console.

Jokes API:

We will be using below URL to get random jokes. That is our publisher will use the below URL to get random jokes and publish it. It is a simple GET request without any authentication.

                  https://joke.deno.dev/                

The response payload is as shown below. Jokes are in the Q & A format.

                  {    "id":120,    "type":"general",    "setup":"How do hens stay fit?",    "punchline":"They always egg-cercise!" }                

Project Set Up:

  • Create a Spring Boot project with below dependencies.

  • I create a multi-module maven project as shown here

Common – DTO:

Let's first create a DTO for the joke. We are interested only in the 'setup' and the 'punchline' from the Joke API.

                  @Data public class Joke implements Serializable {      private static final String JOKE_FORMAT = "Q: %s \nA: %s";      private String setup;     private String punchline;      @Override     public String toString() {         return String.format(JOKE_FORMAT, this.setup, this.punchline);     } }                

Redis PubSub – Publisher:

  • Create a bean for ReactiveRedisOperations
                  @EnableScheduling @SpringBootApplication public class RedisPublisherApplication {      public static void main(String[] args) {         SpringApplication.run(RedisPublisherApplication.class, args);     }      @Bean     public ReactiveRedisOperations<String, Joke> jokeTemplate(LettuceConnectionFactory lettuceConnectionFactory){         RedisSerializer<Joke> valueSerializer = new Jackson2JsonRedisSerializer<>(Joke.class);         RedisSerializationContext<String, Joke> serializationContext = RedisSerializationContext.<String, Joke>newSerializationContext(RedisSerializer.string())                 .value(valueSerializer)                 .build();         return new ReactiveRedisTemplate<String, Joke>(lettuceConnectionFactory, serializationContext);     } }                
  • Then we autowire the ReactiveRedisOperation to publish the message periodically every 3 seconds.
                  @Service public class PublisherService {      private static final String JOKE_API_ENDPOINT = "https://joke.deno.dev/";     private WebClient webClient;      @Autowired     private ReactiveRedisOperations<String, Joke> redisTemplate;      @Value("${topic.name:joke-channel}")     private String topic;      @PostConstruct     private void init(){         this.webClient = WebClient.builder()                 .baseUrl(JOKE_API_ENDPOINT)                 .build();     }      @Scheduled(fixedRate = 3000)     public void publish(){         this.webClient.get()                 .retrieve()                 .bodyToMono(Joke.class)                 .flatMap(joke -> this.redisTemplate.convertAndSend(topic, joke))                 .subscribe();     }  }                

Redis PubSub – Subscriber:

This subscriber is another Spring Boot application. The subscriber part is relatively very simple. Here we subscribe to the channel and we print the value on the console as and when we receive the message. We can include multiple channel names if we are interested.

                  @Service public class SubscriberService {      @Autowired     private ReactiveRedisOperations<String, Joke> reactiveRedisTemplate;      @Value("${topic.name:joke-channel}")     private String topic;      @PostConstruct     private void init(){         this.reactiveRedisTemplate                 .listenTo(ChannelTopic.of(topic))                 .map(ReactiveSubscription.Message::getMessage)                 .subscribe(System.out::println);     }  }                

Dockerizing Infrastructure:

  • I use below Dockerfile to dockerize both publisher and subscriber applications.
                  # Use JRE11 slim FROM openjdk:11.0-jre-slim  # Add the app jar ADD target/*.jar redis-pubsub.jar  ENTRYPOINT java -jar redis-pubsub.jar                
  • docker-compose file with all the dependencies
                  version: '3' services:   redis:     image: redis     ports:       - 6379:6379   publisher:     build: ./redis-publisher     image: vinsdocker/redis-publisher     depends_on:       - redis     environment:       - SPRING_REDIS_HOST=redis   subscriber:     build: ./redis-subscriber     image: vinsdocker/redis-subscriber     depends_on:       - redis     environment:       - SPRING_REDIS_HOST=redis                

Redis PubSub Spring Boot – Demo:

Once everything is ready, I run these commands one by one.

  • Build the project
                  mvn clean package -DskipTests                
  • Build the docker images
                  docker-compose build                
  • Run the applications
                  docker-compose up                

Output:

When I start my subscriber and publisher, I start seeing my subscriber printing all the jokes it receives on the console.

Summary:

We were able to successfully demonstrate Redis PubSub with Spring Boot by developing 2 simple Microservices. As we had seen above the publisher & the subscriber are not tightly coupled, but they still were able to communicate via Redis PubSub feature.

Instead of printing this on a console, how to show this message in a browser? Check this out –Spring WebFlux Streaming

Learn more about Redis.

  • Redis Stream With Spring Boot – Real Time Data Processing
  • Redis Master Slave With Spring Boot
  • Priority-Queue Pattern With Spring Boot + Redis

The source code is available here.

Happy learning 🙂