ActiveMQ支持两种消息投递方式:同步投递和异步投递。消息投递指的是消息生产者端将消息发送到消息服务器(即Broker)的过程,若采用同步投递的方式,则生产者端每次向消息服务器发送消息时都需要同步地等待消息服务器给予消息发送成功与否的回执,这一定程度上可能会引起消息生产者的阻塞,影响消息发送的效率;采用异步投递的方式则不会有这个问题,因此ActiveMQ默认采用异步投递的方式。使用异步投递的方式怎么确定消息投递成功与否呢?ActiveMQ为我们提供了回调机制,在我们向消息服务器发送消息时可以同时传递一个异步回调方法,无论消息是否投递成功消息服务器都会调用这个回调方法用于通知消息生产者这个消息投递的状态。
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.1.3:61616");
factory.setUseAsyncSend(true); //使用异步发送,默认就是true,没必要设
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("queue01");
//需要转换为ActiveMQMessageProducer
ActiveMQMessageProducer messageProducer = (ActiveMQMessageProducer) session.createProducer(queue);
TextMessage textMessage = session.createTextMessage("这是一个消息" + System.currentTimeMillis());
textMessage.setJMSMessageID(UUID.randomUUID().toString()); //自定义messageid
String msgId = textMessage.getJMSMessageID();
//发送消息,传入一个回调接口
messageProducer.send(textMessage,new AsyncCallback() {
@Override
public void onException(JMSException exception) { //发送到mq失败
System.out.println(msgId + "发送失败");
}
@Override
public void onSuccess() { //发送到mq成功
System.out.println(msgId + "发送成功"); //要取我们自己定义msgId
//下面这个MessageID已经变成mq内部的值了,所以我觉得上面那个setJMSMessageID没必要
//System.out.println(textMessage.getJMSMessageID() + "发送成功");
}
});
messageProducer.close();
session.close();
connection.close();
System.out.println("消息发送完毕");
}
显式设置useAsyncSend的值
//1、在brokerUri后跟jms.useAsyncSend参数,更多连接参数参考http://activemq.apache.org/connection-configuration-uri
cf = new ActiveMQConnectionFactory("tcp://locahost:61616?jms.useAsyncSend=true");
//2、设置connectionFactory的useAsyncSend属性,此时要注意ConnectionFactory的类型必须是ActiveMQConnectionFactory
((ActiveMQConnectionFactory)connectionFactory).setUseAsyncSend(true);
//3、设置Connection的useAsyncSend属性,此时要注意Connection的类型必须是ActiveMQConnection
((ActiveMQConnection)connection).setUseAsyncSend(true);
那同步该怎么配置?setUseAsyncSend=false吗?你想得太简单了,先看源码
// ActiveMQSession.java
protected void send(ActiveMQMessageProducer producer, ActiveMQDestination destination,
Message message, int deliveryMode, int priority, long timeToLive,
MemoryUsage producerWindow, int sendTimeout, AsyncCallback onComplete) throws JMSException {
// 省略其他代码
/**
* 异步发送条件
* onComplete:有回调
* sendTimeout:发送超时设置,<=0,默认为0
* msg.isResponseRequired:是否必须响应,默认为false
* connection.isAlwaysSyncSend:是否总是使用同步,默认为false,可以通过factory.setAlwaysSessionAsync(false)设置;
* 后面就是或者的关系了
* msg.isPersistent():消失是否是持久化,默认为true
* connection.isUseAsyncSend():是否使用异步发送,默认为true
* txid:事务id,有事务就是异步
*/
消息的持久类型和和连接模式是或的:所以只要connection配置为异步,就走异步发送
if (onComplete==null && sendTimeout <= 0 && !msg.isResponseRequired() && !connection.isAlwaysSyncSend()
&& (!msg.isPersistent() || connection.isUseAsyncSend() || txid != null)) {
this.connection.asyncSendPacket(msg);
if (producerWindow != null) {
int size = msg.getSize();
producerWindow.increaseUsage(size);
}
} else { // 同步发送
if (sendTimeout > 0 && onComplete==null) {
this.connection.syncSendPacket(msg,sendTimeout);
}else {
this.connection.syncSendPacket(msg, onComplete);
}
}
}
如何测试异步或同步?
//省略其他代码
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
messageProducer.send(textMessage); //如果是同步,这段代码会接收mq服务器的响应,执行时间稍长
}
long end = System.currentTimeMillis();
System.out.println(end-start);
//从时间上就可以区分异步或同步了
疑问!JmsTemplate的AsyncCallback在哪里?从jmsTemplate.send()方法一直到发出消息,都没有看到AsyncCallback
//org.springframework.jms.core.JmsTemplate.java
protected void doSend(Session session, Destination destination, MessageCreator messageCreator)
throws JMSException {
Assert.notNull(messageCreator, "MessageCreator must not be null");
//创建的是MessageProducer,没有转换为ActiveMQMessageProducer
MessageProducer producer = createProducer(session, destination);
try {
Message message = messageCreator.createMessage(session);
if (logger.isDebugEnabled()) {
logger.debug("Sending created message: " + message);
}
doSend(producer, message); //调用发送方法
// Check commit - avoid commit call within a JTA transaction.
if (session.getTransacted() && isSessionLocallyTransacted(session)) {
// Transacted session created by this template -> commit.
JmsUtils.commitIfNecessary(session);
}
}
finally {
JmsUtils.closeMessageProducer(producer);
}
}
protected void doSend(MessageProducer producer, Message message) throws JMSException {
if (this.deliveryDelay >= 0) {
if (setDeliveryDelayMethod == null) {
throw new IllegalStateException("setDeliveryDelay requires JMS 2.0");
}
ReflectionUtils.invokeMethod(setDeliveryDelayMethod, producer, this.deliveryDelay);
}
//发送消息
if (isExplicitQosEnabled()) {
producer.send(message, getDeliveryMode(), getPriority(), getTimeToLive());
}
else {
producer.send(message);
}
}
因此使用JmsTemplate发送消息是同步的,不信你可以参考上面的测试方法测一下。所以一定要启用事务才会异步,当然也可以继承JmsTemplate重写send方法