同步与异步
同步发送
异步发送时很多模式下的默认传输方式,但是发送非事务持久化消息的时候默认使用同步发送模式。同步发送时,Producer.send()方法会被阻塞,知道broker发送一个确认消息给生产者,这个确认消息提示生产者broker已成功将消息路由到目标并把消息保存到二级存储中。
同步发送持久消息可以提供更好的可靠性,但是潜在影响了程序的响应速度,因为在接收到broker的确认消息之前应用程序或线程回被阻塞。如果应用程序能够容忍一些消息的丢失,可以使用异步发送。异步发送不会在收到broker的确认前一直阻塞。
异步发送
使用异步发送可以提高系统性能。在大多数情况下,ActiveMQ都以异步默认发送消息。
例外:在没有使用事务的情况下,生产者以PERSISTENT传送模式发送消息。这种情况下Send方法都是同步的,并且一直阻塞到ActiveMQ发回确认消息。这种确认机制保证消息不会丢失,但会造成生产者阻塞从而影响反应时间。高性能程序一般都能容忍在故障情况下丢失少量数据,可以通过使用异步发送在提高吞吐量。
演示
开启异步投送
启用异步发送需要生产者设置UseAsyncSend=true
这里演示两种方式设置它
// 创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 在连接工厂中设置异步投递
activeMQConnectionFactory.setUseAsyncSend(true);
// 创建连接
ActiveMQConnection connection = null;
connection = (ActiveMQConnection)activeMQConnectionFactory.createConnection();
// 在创建的连接对象中开启异步投递(需要使用ActiveMQConnection)
connection.setUseAsyncSend(true);
丢失消息的场景
connection.setUseAsyncSend(true); //开启异步投递
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("Queue-Async");
ActiveMQMessageProducer producer = (ActiveMQMessageProducer) session.createProducer(queue);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
for (int i = 1; i <= 3; i++) {
TextMessage message = session.createTextMessage("Message ==> " + i);
producer.send(message);
}
// 如果开启事务,则需要在会话关闭前手动提交事务
session.commit();
以上代码中设置了异步投递但发送是生产者使用producer.send(message);
持续发送消息。由于消息不阻塞,生产者会认为所有send消息均被发送成功至MQ。如果服务器突然宕机,此时生产者端内存中尚未发送到MQ的消息将全部丢失。正确的异步发送方法是需要接收回调的。
异步投递确保MQ收到消息
正确的异步发送方法是需要接收回调的。
producer.send中有带有AsyncCallback的方法。
public void send(Message message, AsyncCallback onComplete) throws JMSException
该方法需要重写onSuccess
方法和onException
方法。
- onSuccess():表示这条消息成功发送到了MQ上,并且接收到了MQ持久化后的回调。
- onException():MQ返回一个入队异常的回执。
生产者代码示例
public class JmsProduce_AsyncSend {
public static final String ACTIVEMQ_URL = "nio://192.168.100.110:61608";
// public static final String ACTIVEMQ_URL = "tcp://localhost:61616";
// public static final String ACTIVEMQ_URL = "failover:(tcp://host110:61616,tcp://host111:61616,tcp://host112:61616)?randomize=false";
public static final String QUEUE_NAME = "queue01-Cluster";
public static void main(String[] args) {
// 创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 创建连接
ActiveMQConnection connection = null;
Session session = null;
try {
connection = (ActiveMQConnection)activeMQConnectionFactory.createConnection();
// 开启异步投递
connection.setUseAsyncSend(true);
log.info("连接创建");
// 开启连接
connection.start();
log.info("连接启动");
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Queue queue = session.createQueue(QUEUE_NAME);
// 使用ActiveMQMessageProducer创建消息生产者
ActiveMQMessageProducer producer = (ActiveMQMessageProducer) session.createProducer(queue);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
// 使用消息生产者,生产三条消息发送到MQ队列
for (int i = 1; i <= 3; i++) {
// 创建消息
TextMessage message = session.createTextMessage("Cluster-Message ==> " + i);
// 设置消息ID
message.setJMSMessageID(UUID.randomUUID().toString() + "--orderXJY");
// 通过消息生产者发布
String msgID = message.getJMSMessageID();
producer.send(message, new AsyncCallback() {
@Override
public void onSuccess() {
System.out.println(msgID + "====> Success");
}
@Override
public void onException(JMSException e) {
System.out.println(msgID + "====> Failed");
}
});
}
// 如果开启事务,则需要在会话关闭前手动提交事务
session.commit();
} catch (JMSException e) {
try {
if (session != null) {
session.rollback();
}
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
throw new RuntimeException(e);
} finally {
if (session != null) {
try {
session.close();
connection.close();
} catch (JMSException e) {
System.exit(1);
}
}
}
log.info("连接断开");
}
}