ActiveMQ的可靠性
控制消息签收(Acknowledgment)
客户端成功接收一条消息的标志是这条消息被签售。成功签收一条消息一般包括如下三个阶段:
- 客户端接收消息;
- 客户端处理消息;
- 消息被签收。签收可以由ActiveMQ发起,也可以由客户端发起,取决于Session签收模式的设置。
-
指定消息传送模式
详见ActiveMQ消息传送机制-设置方式
-
签收模式的设置
再带事务的Session中,签收自动发生在事务提交时,如果不提交事务,在系统重启后消息会再次发送。如果事务回滚,所有已经连接的消息将会被再次传送。在不带事务的Session中,一条消息何时和如何被签收取决于Session的设置。
-
Session.AUTO_ACKNOWLEDGE
自动确认,客户端发送和接收消息不需要做额外的工作。 当客户端从receive或OnMessage成功返回时,Session自动签收客户端的这条消息的收条。在AUTO_ACKNOEWLEDGE的Session中,同步接收reveive是上述三个阶段的一个例外,在这中情况下,收条和签收紧随在处理消息之后发生。
-
Session.CLIENT_ACKNOWLEDGE
客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会删除消息。 客户端通过调用消息的acknowledge方法签收消息。在Session层面:签收一个以消费的收条会自动地签收这个Session所有已消费消息的收条。
-
Session.DUPS_OK_ACKNOWLEDGE
此选项只是Session不必确保对传送消息的签收,消息可能会重复发送。它可能引起消息的重复,但是降低了Session的开销,所以只有客户端能容忍重复的消息,才可以使用(如果ActiveMQ再次传送同一消息,那么消息头中的JMSRedelivered将设置为true,客户端据此可进行消息的重复处理控制,在需要考虑资源使用时,这种模式非常有效)。
-
-
设置签收模式
Session session=connection.createSession(paramA,paramB);
paramA是设置事务的,为true时:paramB的值忽略, acknowledgment mode被jms服务器设置为SESSION_TRANSACTED 。
paramA设置为false时:paramB的值可为Session.AUTO_ACKNOWLEDGE,Session.CLIENT_ACKNOWLEDGE,DUPS_OK_ACKNOWLEDGE其中一个。
-
持久化订阅
消息的两种模式,点对点(queue)和发布/订阅(topic),queue模式下,消息是会被持久化到磁盘,而topic模式下,消息会随着服务的停止而消失,但是某些场景下,我们想将topic消息也进行持久化,只需要进行如下改动。
生产者端:只需要将目的地创建成Topic模式即可,其他不用变 session.createTopic(“xxxx”);
消费者端:需要为连接指定一个ID,创建持久订阅的目的地和消费者
ConnectionFactory factory = new ActiveMQConnectionFactory(USERNAME,PASSWORD,BROKER_URL); Connection conn = factory.createConnection(); //需要一个ID conn.setClientID("TopicConsumerTwo"); conn.start(); //不启用事务,自动应答 Session session = conn.createSession(false,Session.AUTO_ACKNOWLEDGE); //创建目的地,这里如果想使用持久订阅,创建topic模式的目的地 Topic destination = session.createTopic("persistent-topic"); //创建消费者-持久订阅的消费者 TopicSubscriber topicSubscriber = session.createDurableSubscriber(destination, "any-name"); topicSubscriber.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { try { String msg = ((TextMessage)message).getText(); System.out.println("接收消息:"+msg); } catch (JMSException e) { e.printStackTrace(); } }
-
双向应答
双向应答可以这样描述:
- producer–>发送消息到broker, 然后等待确认消息
- customer–>从broker 获得消息,然后发送确认消息—>broker
- producer --> 从broker 获得确认消息
生产者代码:
// 承接刚才的代码,发送消息的时候,需要填写一个回执的地址 Destination recall_destination = session.createQueue("recall_queue"); // 将回执地址写在消息里面,方便李四知道 message.setJMSReplyTo(recall_destination); producer.send(message); // 发送之后,某个地方这里变成消息消费者,等待那边给我发送确认消息 MessageConsumer replyConsumer =session.createConsumer(recall_destination); // 这里我们用个消息监听 // 异步接收 replyConsumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { System.out.println(textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } }); // 同步接收 replyConsumer.receive();
消费者代码:
// 获得回执地址 Destination recall_destination = message.getJMSReplyTo(); // 创建回执消息 TextMessage textMessage = session.createTextMessage("张三,我已经收到消息了"); // 以上收到消息之后,从新创建生产者,然后在回执过去 MessageProducer producer = session.createProducer(recall_destination); producer.send(textMessage);
-
JmsTemplete实现双向应答
发送
public void sendMessage(String str) throws JMSException {
TextMessage replyMessage = (TextMessage) jmsTemplate.sendAndReceive(queue, session -> {
Message message = session.createTextMessage(str);
message.setJMSReplyTo(replyQueue);
return message;
});
System.out.println(replyMessage.getText());
}
接收
public void receiveMessage() throws JMSException {
TextMessage message = (TextMessage) jmsTemplate.receive(queue);
System.out.println(message.getText());
Destination destination = message.getJMSReplyTo();
jmsTemplate.send(destination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
Message message=session.createTextMessage("应答消息");
return message;
}
});
- JMSDestination:消息发送的目的地:主要是指Queue和Topic,自动分配
- JMSDeliveryMode:传送模式。有两种 :持久模式和非持久模式(persistent或nonpersistent)。前者表示消息在被消费之前,如果JMS提供者DOWN了,重新启动后消息仍然存在。后者在这种情况下表示消息会被丢失。可以通过下面的方式设置:
Producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); - JMSExpiration:表示一个消息的有效期,只有点对点模式中可设置。只有在这个有效期内,消息消费者才可以消费这个消息。默认值为0,表示消息永不过期。可以通过下面的方式设置:
producer.setTimeToLive(3600000); //有效期1小时 (1000毫秒 * 60秒 * 60分) - JMSPriority:消息优先级,从 0-9 十个级别,0-4 是普通消息,5-9 是加急消息。JMS 不要求JMS Provider 严格按照这十个优先级发送消息,但必须保证加急消息要先于普通消息到达。默认是4级。自动分配
- JMSMessageID:一个字符串用来唯一标示一个消息,J主要是用来关联多个Message,例如需要回复一个消息的时候,通常把回复的消息的JMSCorrelationID设置为原来消息的ID。
- JMSTimestamp:一个JMS Provider在调用send()方法时自动设置的。它是消息被发送和消费者实际接收的时间差。自动分配
- JMSCorrelationID:用来连接到另外一个消息,典型的应用是在回复消息中连接到原消息。在大多数情况下,JMSCorrelationID用于将一条消息标记为对JMSMessageID标示的上一条消息的应答,不过,JMSCorrelationID可以是任何值,不仅仅是JMSMessageID。由开发者设置session.createConsumer(queue,“JMSCorrelationID=’” + message.getJMSMessageID() + “’”);
- JMSReplyTo:有时消息生产者希望消费者回复一个消息,JMSReplyTo为一个Destination,表示需要回复的目的地。当然消费者可以不理会它。
- JMSType:表示消息体的结构,和JMS提供者有关。
- JMSRedelivered:如果一个客户端收到一个设置了JMSRedelivered属性为true的消息,则表示可能客户端曾经在早些时候收到过该消息,但并没有签收(acknowledged)。如果该消息被重新传送,JMSRedelivered=true反之,JMSRedelivered =false。自动设置