简体   繁体   中英

How do the Sender know that the Receiver isn't available in JMS using Spring Boot?

I have a Spring Boot app, there are 2 microservices and these microservices communicate asynchronous using JMS and ActiveMq. So the Sender (ms1) sends a message to the Receiver (ms2), the Sender will put the message on the queue and if the Receiver isn't available, the message will stay on the queue until the Receiver is available.

I want to ask you how could the Sender knows if the Receiver is available or not? I want to know that because I want to use Hystrix and if the Receiver is available the Sender will show a message like this: "Transaction successfully completed,", but if the Receiver isn't available the Sender will show other message: something like this, "The Receiver service isn't currently availble. the message is added to the queue and will be sended to the Receiver when it'll be available".

This code is from the Sender service:

    @HystrixCommand(fallbackMethod="sendMessageToProducerFail")
    private ResponseEntity sendMessageToProducer(String jsonStr) {
        jmsTemplate.convertAndSend(queue, jsonStr);
        return new ResponseEntity("Transaction successfully completed!", HttpStatus.CREATED);
    }

    private ResponseEntity sendMessageToProducerFail(String jsonStr) {
//        "The Receiver service isn't currently availble, the message is added to the queue..."
    }

There's no simple way to achieve this. Identifying whether the consumer is up or not bring some of the design choices. Also, there's no out of box solution for your problem. You can use the concept of a heartbeat to notify producers about consumer state.

+----------+               +-------------+              +--------+
|          |<--Heartbeat---|             |---Message--->|        |
| Producer |               | AMQP Server |              |Consumer|
|          |----Message--->|             |<--Heartbeat--|        |  
+----------+               +-------------+              +--------+

Your setup would look somewhat like this, In this producer( ms1 ) will send messages to AMQP server and will consume Heartbeat events from the consumer(s), to identify whether the consumer(s) is/are alive. This will become more tricky when you have more than one producer/consumer for the same topic.

Once you have more than once consumers, multiple heartbeats would be sent on the same topic/queue. You need to identify which one of them is alive, you need to also consider consumers going down/up.

Each heartbeat can have timestamp when it was generated so that you can make a consumer alive decision at a more granular level. The next question would be when to send a heartbeat? As you're using Spring boot you will not have the flexibility of sending heartbeat outside of the message listener. If you're using the MessageListener interface then do something like this.

Create a heartbeat publisher class

@Component
class HeartBeatPublisher {
   public void registerMessageListener( String listenerName,
                                        String topicOrQueueName,
                                        Long intervals) {
   // store this detail in a map 
   // do lazy init of threads to send heartbeat 
   }
}

Your message listener class would like

@Component
class MyMessageListener implements MessageListener {
   @Autowired HeartBitPublisher heartBeatPublisher;
   @PostConstruct
   public void init(){
      // 30 seconds interval
      heartBeatPublisher.registerMessageListener("MyMessageListener", 
                                                 "MyMessageListener",
                                                 30*1000 );
   }
   void onMessage(Message message){
     // consume message here
   }
}

In case you're not using MessageListener still you can send heartbeats, in that case, I would recommend adding one listener per component.

@Component
class MyMessageListener{
   @Autowired HeartBeatPublisher heartBeatPublisher;
   @PostConstruct
   public void init(){
     // 30 seconds interval
     heartBeatPublisher.registerMessageListener("MyMessageListener", 
                                                "MyMessageListener",
                                                30*1000 );
   }

   @RabbitListener(queues="myQueue")
   public void onMessage(Object message) {
    // Consume message
   }
}  

On the producer side, you need to add a listener(s) to check which consumer(s) is/are active using a heartbeat. Once you know which consumer(s) is/are active you can use that to restrict message publish.

@Component
class HeartBeatListener {
  private List<String> queues;
   @PostConstruct 
   public void init(){
      // initialize queue and consumer status to inactive
      // at certain intervals checks for missing heartbeat 
      // if you find heartbeats are not being sent that means either 
      // that consumer has died or there's a delay in heart bit 
      // publish in that case mark that consumer/topic/queue inactive
   }
  
   @RabbitListener(queues="myQueue-HeartBeat")
   public void onMessage(Object message) {
     // update consumer status 
     // Record heart beat for a given consumer/topic
   }
}  

Instead of doing all this, you can use Consul , Zookeeper , or Etcd to move some of the works to these systems.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM