11、高级特性
11.1、异步投递
(1)什么是异步投递
总结:
① 异步发送可以让生产者发的更快。
② 如果异步投递不需要保证消息是否发送成功,发送者的效率会有所提高。如果异步投递还需要保证消息是否成功发送,并采用了回调的方式,发送者的效率提高不多,这种就有些鸡肋。
(2)三种方式
// 方式1
private static final String ACTIVEMQ_URL = "tcp://118.24.20.3:61626?jms.useAsyncSend=true";
// 方式2
activeMQConnectionFactory.setUseAsyncSend(true);
// 方式3
((ActiveMQConnection)connection).setUseAsyncSend(true);
(3)异步发送如何确认发送成功
异步发送的回调
activeMQConnectionFactory.setUseAsyncSend(true); // 设置异步发送
ActiveMQMessageProducer messageProducer = (ActiveMQMessageProducer) session.createProducer(queue);
try {
for (int i = 0; i < 3; i++) {
TextMessage textMessage = session.createTextMessage("QUEUQ---第" + i + "条message!");
textMessage.setJMSMessageID(UUID.randomUUID().toString());
final String msgID = textMessage.getJMSMessageID();
messageProducer.send(textMessage, new AsyncCallback() {
// 重载的send方法
@Override
public void onSuccess() {
System.out.println("发送成功的消息ID:" + msgID);
}
@Override
public void onException(JMSException e) {
System.out.println("发送失败的消息ID:" + msgID);
}
});
}
System.out.println("消息发送完成!");
} catch (Exception e) {
e.printStackTrace();
} finally {
activeMQConnectionFactory.close();
session.close();
connection.close();
}
11.2、延迟投递和定时投递
(1)介绍
可以设置的具体延时和定时参数
具体见官网文档:http://activemq.apache.org/delay-and-schedule-message-delivery.html
(2)修改配置文件
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulerSupport="true" >
(3)代码实现
生产者
public class JmsProduce_delay {
private static final String ACTIVEMQ_URL = "tcp://192.168.5.130:61608";
private static final String ACTIVEMQ_QUEUE_NAME = "Schedule01";
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(ACTIVEMQ_QUEUE_NAME);
MessageProducer messageProducer = session.createProducer(queue);
long delay = 10*1000;
long period = 5*1000;
int repeat = 3 ;
try {
for (int i = 0; i < 3; i++) {
TextMessage textMessage = session.createTextMessage("tx msg--" + i);
// 延迟的时间
textMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, delay);
// 重复投递的时间间隔
textMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD, period);
// 重复投递的次数
textMessage.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, repeat);
// 此处的意思:该条消息,等待10秒,之后每5秒发送一次,重复发送3次。
messageProducer.send(textMessage);
}
System.out.println("消息发送完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
messageProducer.close();
session.close();
connection.close();
}
}
}
消费者
public class JmsConsumer {
private static final String ACTIVEMQ_URL = "tcp://192.168.5.130:61608";
private static final String ACTIVEMQ_QUEUE_NAME = "Schedule01";
public static void main(String[] args) throws JMSException, IOException {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(ACTIVEMQ_QUEUE_NAME);
MessageConsumer messageConsumer = session.createConsumer(queue);
messageConsumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
TextMessage textMessage = (TextMessage) message;
System.out.println("***消费者接收到的消息: " + textMessage.getText());
textMessage.acknowledge();
} catch (Exception e) {
System.out.println("出现异常,消费失败,放弃消费");
}
}
}
});
System.in.read();
messageConsumer.close();
session.close();
connection.close();
}
}
11.3、消息消费的重试机制
(1)概念
消费者收到消息,之后出现异常了,没有告诉broker确认收到该消息,broker会尝试再将该消息发送给消费者。尝试n次,如果消费者还是没有确认收到该消息,那么该消息将被放到死信队列重,之后broker不会再将该消息发送给消费者。
(2)具体哪些情况会引发消息重发
① Client用了transactions且再session中调用了rollback
② Client用了transactions且再调用commit之前关闭或者没有commit
③ Client再CLIENT_ACKNOWLEDGE的传递模式下,session中调用了recover
(3)消息重发时间间隔和重发次数
间隔:1
次数:6
每秒发6次
(4) 有毒消息Poison ACK
一个消息被redelivedred超过默认的最大重发次数(默认6次)时,消费者会向MQ发一个“poison ack”表示这个消息有毒,告诉broker不要再发了。这个时候broker会把这个消息放到DLQ(私信队列)。
(5)属性说明
(6)代码验证
生产者发送3条数据
消费者。开启事务,却没有commit。重启消费者,前6次都能收到消息,到第7次,不会再收到消息。
public class Jms_TX_Consumer {
private static final String ACTIVEMQ_URL = "tcp://192.168.5.130:61616";
private static final String ACTIVEMQ_QUEUE_NAME = "dead01";
public static void main(String[] args) throws JMSException, IOException {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
final Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(ACTIVEMQ_QUEUE_NAME);
MessageConsumer messageConsumer = session.createConsumer(queue);
messageConsumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("***消费者接收到的消息: " + textMessage.getText());
//session.commit();
}catch (Exception e){
e.printStackTrace();
}
}
}
});
//关闭资源
System.in.read();
messageConsumer.close();
session.close();
connection.close();
}
activemq管理后台。多了一个名为ActiveMQ.DLQ队列,里面多了3条消息。
(7)修改默认参数
// 修改默认参数,设置消息消费重试3次
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setMaximumRedeliveries(3);
activeMQConnectionFactory.setRedeliveryPolicy(redeliveryPolicy);
(8)spring整合配置信息
11.4、死信队列
(1)介绍
官网文档: http://activemq.apache.org/redelivery-policy
死信队列:异常消息规避处理的集合,主要处理失败的消息。
(2)死信队列的配置(一般采用默认)
-
sharedDeadLetterStrategy:不管是queue还是topic,失败的消息都放到这个队列中。下面修改activemq.xml的配置,可以达到修改队列的名字。
-
individualDeadLetterStrategy:可以为queue和topic单独指定两个死信队列。还可以为某个话题,单独指定一个死信队列。
-
自动删除过期消息:过期消息是指生产者指定的过期时间,超过这个时间的消息。
-
存放非持久消息到死信队列中