redis pubsub spring boot

Redis PubSub With 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.

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 (Subscribers) 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.

The source code is available here.

Happy learning 🙂

 

Share This:

3 thoughts on “Redis PubSub With Spring Boot

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.