Skip to main content

One post tagged with "eventdriven"

View All Tags

· 5 min read

The Story

Sample Message Broker App

Imagine we're building a simple e-commerce application. When a customer places an order, it's not instantly whisked away by elves. Instead, the order details – a message filled with product information, shipping address, and payment details – gets sent to a queue managed by a message broker.

Meanwhile, our order processing system sits like a hungry rabbit, constantly checking the queue for new messages. Once it grabs an order message, it springs into action: verifying payment, notifying the warehouse, and sending updates to the customer. All without the two systems ever needing to directly talk to each other!

This decoupling is the superpower of message brokers. Applications don't need to know the specifics of each other's internal workings. They simply send and receive messages, leaving the orchestration to the broker. This makes systems more flexible, scalable, and resilient.

Let's delve deeper into this rabbit hole, using RabbitMQ as our trusty guide.

The Technology

RabbitMQ is a popular open-source message broker, and it's a great starting point to understand the magic behind these event-driven systems.

  • It is used worldwide at small startups and large enterprises.
  • It is lightweight and easy to deploy on premises and in the cloud.
  • It can be deployed in distributed and federated configurations to meet high-scale, high-availability requirements.
  • It runs on many operating systems and cloud environments, and provides a wide range of developer tools for most popular languages.

The Concepts

RabbitMQ Concepts

  • Message: It is the fundamental unit of communication in RabbitMQ. It contains the data being sent from the producer to the consumer. It is like a post carrying a message.
  • Producer: A producer is an application or component that sends messages to RabbitMQ. It is like a person sending the post.
  • Consumer: A consumer is an application or component that receives and processes messages from RabbitMQ. It is a person receiving the post.
  • Queue: A queue is a buffer that stores messages until they are consumed. Messages are placed in queues by producers and retrieved by consumers. It is a postbox that stores messages of a person.
  • Exchange: An exchange is a routing mechanism that receives messages from producers and routes them to queues. It is like a post office.
  • Routing Key: A routing key is a property of a message that is used by exchanges to determine which queues should receive the message. This is like a mailing address for a post.

The Tutorial

Generate prototype from WeDAA

Use below Architecture as reference and generate a project from WeDAA

All the code mentioned in the blog will be generated by WeDAA. It can be further extended as necessary.

Sample RabbitMQ WeDAA Architecture

RabbitMQ Configuration

RabbitMQConfigOrdersToInventory class in orders service registers Queue, Exchange, Binding and Message Converters are as beans for auto-configuration in Spring AMPQ.

@Configuration
public class RabbitMQConfigOrdersToInventory {

public static final String QUEUE = "OrdersToInventory_message_queue";
public static final String EXCHANGE = "OrdersToInventory_message_exchange";
public static final String ROUTING_KEY = "OrdersToInventory_message_routingKey";

@Bean
public Queue queueOrdersToInventory() {
return new Queue(QUEUE);
}

@Bean
public TopicExchange exchangeOrdersToInventory() {
return new TopicExchange(EXCHANGE);
}

@Bean
public Binding bindingOrdersToInventory() {
return BindingBuilder.bind(this.queueOrdersToInventory()).to(this.exchangeOrdersToInventory()).with(ROUTING_KEY);
}

@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}

@Bean
public AmqpTemplate template(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(messageConverter());
return template;
}
}

Message Producer

RabbitMQProducerOrdersToInventory class in orders service sends a message to the exchange every 15 seconds.

@Scheduled(cron = "0/15 * * * * *")
public void publishMessage() {
RabbitMessageModel message = new RabbitMessageModel();
message.setMessage("Publishing this message from orders with key: " + RabbitMQConfigOrdersToInventory.QUEUE);
message.setDateTime(new Date());
template.convertAndSend(RabbitMQConfigOrdersToInventory.EXCHANGE, RabbitMQConfigOrdersToInventory.ROUTING_KEY, message);
logger.info("Message Published Successfully");
}

Message Consumer

RabbitMQConsumerOrdersToInventory in the inventory service starts receiving the messages as the messages are sent by the Producer.

msgs, err := channel.Consume(
queueName,
"",
true,
false,
false,
false,
nil,
)

forever := make(chan bool)
go func() {
for d := range msgs {
logger.Infof("Received Message: %s\n", d.Body)
}
}()
<-forever

The execution

  1. Bootup the RabbitMQ server

    WeDAA provides dockerfile for starting RabbitMQ server quickly. It can be found in both inventory and orders service.

    RabbitMQ server can be started using below command from orders service.

    docker compose -f src/main/docker/rabbitmq.yml up --wait

    RabbitMQ's management console can be accessed on http://localhost:15672/

    Default username: guest, password: guest

  2. Start the orders service

    In the sample architecture, orders service acts as producer. Start the service using the following command

    ./mvnw

    Once the service is started, it can be seen from the logs that messages are sent periodically.

     2024-01-15T20:35:00.015+05:30  INFO 55955 --- [rs-scheduling-1] .o.c.r.RabbitMQProducerOrdersToInventory : Message Published Successfully 
    2024-01-15T20:35:15.008+05:30 INFO 55955 --- [rs-scheduling-1] .o.c.r.RabbitMQProducerOrdersToInventory : Message Published Successfully
    2024-01-15T20:35:30.003+05:30 INFO 55955 --- [rs-scheduling-1] .o.c.r.RabbitMQProducerOrdersToInventory : Message Published Successfully
    2024-01-15T20:35:45.002+05:30 INFO 55955 --- [rs-scheduling-1] .o.c.r.RabbitMQProducerOrdersToInventory : Message Published Successfully
  3. Start the inventory service

    In the sample architecture, inventory service acts as consumer.

    Build and start the service using the following commands

    go mod tidy
    go run .

    Once started, inventory service starts consuming the messages sent by orders service.

     2024-01-15 20:41:33  file=rabbitmq/RabbitMQConsumerOrdersToInventory.go:51 level=info Received Message: {"id":1,"message":"Publishing this message from orders with key: OrdersToInventory_message_queue","dateTime":1705331085013}
    2024-01-15 20:41:33 file=rabbitmq/RabbitMQConsumerOrdersToInventory.go:51 level=info Received Message: {"id":2,"message":"Publishing this message from orders with key: OrdersToInventory_message_queue","dateTime":1705331100012}
    2024-01-15 20:41:33 file=rabbitmq/RabbitMQConsumerOrdersToInventory.go:51 level=info Received Message: {"id":3,"message":"Publishing this message from orders with key: OrdersToInventory_message_queue","dateTime":1705331115005}
    2024-01-15 20:41:33 file=rabbitmq/RabbitMQConsumerOrdersToInventory.go:51 level=info Received Message: {"id":4,"message":"Publishing this message from orders with key: OrdersToInventory_message_queue","dateTime":1705331130001}
  4. Track activity on RabbitMQ management console

RabbitMQ Exchange

RabbitMQ Queue

The Conclusion

This blog gives a head start on making use of RabbitMQ to orchestrate your event-driven microservice application architectures.

Learn more from: https://www.rabbitmq.com/getstarted.html