Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Sending and Receiving Messages

...

  • Direct - will forward messages to bound queues if the message routing key matches the queue name. The routing key pattern on the binding is not requiredin the binding.
  • Default  - The Default Exchange is a special Direct Exchange which doesn't have any explicit bindings. The message is sent to the Queue whose name matches the routing key. The Default Exchange has an empty name. The Default Exchange cannot be deleted.
  • Fanout - will forward all messages to bound queues. The routing key pattern on the binding is not required.
  • Topic - used for pubpublish/sub subscribe - will forward to downstream queues/exchanges where the message's routing key matches the routing key pattern on the binding between the Queue Topic Exchange and the Topic Exchangebound, downstream Queues/Exchanges.
  • HeaderA headers exchange routes messages based on arguments containing headers and optional values. Headers exchanges are very similar to topic exchanges, but route messages based on header values instead of routing keys. Headers Exchange can match on any or all of a list of header/value pairs. In addition to custom headers, Header Exchanges can route based the following RabbitMQ message properties too.
    • content_type
    • content_encoding
    • priority
    • correlation_id
    • reply_to
    • expiration
    • message_id
    • timestamp
    • type
    • user_id
    • app_id
    • cluster_id

Exchanges can be bound to Queues or other Exchanges.
Exchanges and Queues are linked via Bindings - sometimes the Binding uses a Routing Key Pattern. The Message's Routing Key can be a single word. More generally, it can be several words delimited by dots. The Routing Key pattern supports * for any 1 word and also # for any number of words.

When you configure an exchange Exchange - you can specify an Alternative Exchange where messages sent to this exchange but not routed by this exchange (to another Exchange or Queue) are sent. This is different but related to Dead Letter Exchanges where messages that are rejected by Q Queue consumers (or timed out on QQueue, or rejected because of full Q Queue ) are sent

Direct Messaging

To send a message to a specific Queue - you send the message to the 'default exchangeDefault Exchange' using the Queue Name as the 'routing key'. There is no routing key pattern required on the an implicit binding between the Direct Default Exchange and the Queues it's bound tooevery Queue.

This is not flexible because it ties the producer to a fixed, single consumer.

...

Publish/Subscribe (pub/sub) Messaging

Publish/Subscribe decouples a single producer from many (potential) receivers.

...


Experimenting with the integration of RabbitMQ and Spring - I was able to get the following working:

Using RabbitMQ using docker image and docker compose.

...

When connecting to rabbit from spring - the following properties are used

Code Block
titleSpring Rabbit Properties
spring.rabbitmq.host=localhost

...


spring.rabbitmq.port=5672

...


spring.rabbitmq.username=<username>

...


spring.rabbitmq.password=<password>
spring.rabbitmq.virtual-host=/

Configuring Queues/Exchanges in RabbitMQ

You can create Queues/Exchanges via the admin web interface. This is okay for experiments.
You can create Queues/Exchanges via Spring using the org.springframework.amqp.core.AmqpAdmin class. This is okay for experiments BUT you probably don't want services creating their own queues. This probably relies on the rabbit RabbitMQ user having 'admin' privileges too.
You can create Queues/Exchanges by using the rabbitmqadmin command locally and point it to the RabbitMQ server using --host and --port parameters.

You can create Queues/Exchanges by using the rabittctl command rabbitmqadmin command within the docker container running rabbit.
You can export Rabbit Broker Definitions json file from the web interface and import one too. Looks like there is another The command line tool called rabbitmqadmin which can tool rabbitmqadmin can be used to export/import configsconfiguration. This is probably what we want to have configuration as code.

...

Rabbit Payloads are an array of bytes. So even for String based messaging - without Without a converter, you have to convert a String messages to/from bytes. Spring's RabbitTemplate performs the conversion for String payloads.
It was straightforward to send messages to Queues using org.springframework.amqp.rabbit.core.RabbitTemplate
It was straightforward to recv messages from Queues using the annotation : org.springframework.amqp.rabbit.annotation.RabbitListener

Pub/Sub using a single Topic Exchange

...

PreSend/Post Receive Message Post Processors

It's possible to add pre-send and post-

...

receive Post Processors. These can be used to reduce boiler plate by, for example, adding 'standard' properties to messages - such as the name of the micro service sending the message. I've been able to get the pre-send post-processor working but not the post-receive post-processor.

Mapping JSON messages to Java Objects

If you define a Spring bean which is an instance of org.springframework.amqp.support.converter.Jackson2JsonMessageConverter. Then conversion between JSON message payloads and Java objects is done automatically by Spring. 
For sending use RabbitTemplate.sendAndConvert
For Receiving use the '@MessageListener@RabbitListener' annotation to specify which queue(s) you want to receive from and just the the Java type in the method.
For example to read json JSON messages from a Queue that are automatically converted into Customer instances:


@RabbitListener(queues = RabbitInfo.QUEUE_CUSTOMERS)
public void receiveCustomer(Customer customer, Message msg) {

This works well when you only want to receive 1 type of message from a Queue. The following does not work:

  1. simple having multiple methods annotated with '@MessageListener@RabbitListener' trying to read different objects from the same queue. Extra setup is required for this.
  2. using a 'parent type' in the hope that Spring will be able to deliver subclass instances related to different messages.

...

2) Use a conversion tool to convert JSON schema to JSR303 (Standard Java Validation) annotated POJOS. Then we can use '@Valid' to  to validate messages when we receive them. This does work, it keeps the code tidy. The downside here is that JSON Schema is more sophisticated that JSR303 annotations. The tool to convert JSON Schema Json Schema 2 pojo can be used online, as a command line tool and even as a Maven plugin.

I was able to generate POJOS for the Reconciliation and Position messages defined on this page Message Format and Examples. Using these 2 messages - the JSR303 annotations did a good job of reflecting the  JSON schema.

...

Be able to startup rabbit to a known configuration of Queues/Exchanges. see Configuring RabbitMQ Resources

Be able to run a single command to purge all queues. This might be handy for integration testing. see Configuring RabbitMQ Resources

Be able to have Spring read different types of messages from the same queue and automatically bind these messages to Java objects and validate them.

...

Investigate how we might test Rabbit/Spring integration. To test sent messages and invoke receive endpoints. - Done with Test Containers- needs documenting.

At some point, we may be able to create a Spring/Rabbit support library. - Done in https://github.com/Health-Education-England/TIS-rabbit-mongo-spring-boot-starter

DeadLetterQueues / TTL / Durability - Done a demo with DLQ and TTL - needs documenting.

QUESTIONS

Is a simple Pub/Sub configuration using 1 Topic Exchange enough?

...