MQ存在解决的问题:
如果一个系统是客户提交一个请求,服务器端就立马要响应,这样是客户端和服务器端之间生命周期的强相关。
但是一般情况下客户端提交请求只要服务器端记得做就行,不一定非要严格地请求到来立马处理并应答。这种情形类似于去取快递,有时你在上班,希望快递人员把包裹放在一个代取点,等你有时间再去取。
还有的情形是强相关带来的不好的地方,如果客户提交的请求因为网络原因,服务器端的故障等延期或丢失,那么服务器端会重复或者丢失处理。这样就会带来问题。
为了解决上述问题,引入了消息中间件机制,也叫做消息队列。Message Queue
activeMQ 是消息队列的一种
消息队列大致有两种类型,1.点对点模式,2.订阅发布模式
点对点模式是只要一个消息被一个消费者消费掉,就会把这条消息从消息队列移出。
订阅发布模式是一条消息可以被多个消费者消费。
简单的点对点模式Demo:
Producer.java:
package com.anlysqx.producer;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.ServerSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class Producer {
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
private static final String BROKEURL = "tcp://127.0.0.1:61616";
private static final String QUEUENAME = "myQueue";
public static void start() throws JMSException, InterruptedException{
//获取ActiveMQ会话工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
//获取连接
Connection createConnection = activeMQConnectionFactory.createConnection();
//启动连接
createConnection.start();
//不启用事务,jms 设置消息可靠性 自动签收
Session createSession = createConnection.createSession(true, Session.AUTO_ACKNOWLEDGE);
//创建队列
Queue createQueue = createSession.createQueue(QUEUENAME);
//创建一个生产者
MessageProducer createProducer = createSession.createProducer(createQueue);
//放置消息
for (int i = 0; i < 5; i++) {
//设置存放消息队列内容
TextMessage createTextMessage = createSession.createTextMessage("hello liaoyuan this is your first mq i:"+i);
createProducer.send(createTextMessage);
System.out.println("消息队列存放消息成功... i"+i);
createSession.commit();
Thread.sleep(100);
}
createSession.close();
createConnection.close();
}
public static void main(String[] args) throws JMSException, InterruptedException {
start();
}
}
consumer.java:
package com.anlysqx.consumer;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class Consumer {
private static final String USERNAME = "admin";
private static final String PASSWORD = "admin";
private static final String BROKEURL = "tcp://127.0.0.1:61616";
private static final String QUEUENAME = "myQueue";
public static void start() throws JMSException{
//获取ActiveMQ会话工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
//获取连接
Connection createConnection = activeMQConnectionFactory.createConnection();
//启动连接
createConnection.start();
//不启用事务,jms 设置消息可靠性 自动签收
Session createSession = createConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
//创建队列
Destination destination = createSession.createQueue(QUEUENAME);
//创建一个消费者
MessageConsumer createConsumer = createSession.createConsumer(destination);
while(true){
//阻塞方式接收消息
TextMessage textMessage = (TextMessage) createConsumer.receive();
if(textMessage != null){
//打印消息
System.out.println("消费者消费消息,内容:"+textMessage.getText());
textMessage.acknowledge();
}else{
break;
}
}
createSession.close();
createConnection.close();
}
public static void main(String[] args) throws JMSException {
start();
}
}
上面的代码流程,生产者和消费者大致相同。非常类似Mybatis的session创建过程
首先得到连接工厂由连接工厂生成连接对象,连接对象start,连接对象生成session对象,session对象生成消息队列,session对象根据消息队列生成一个生产者,创建消息,生产者发送消息。最后关闭session,关闭connection。
需要注意的地方,
1.如果 Session createSession = createConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
false 代表不使用事务, Session.CLIENT_ACKNOWLEDGE代表当消息消费完成向MQ手动发送确认消息。
为了保证消息幂等性:
1.开启事务和Session.AUTO_ACKNOWLEDGE自动确认,再发送消息或消费消息后手动session.commit,这样一个事务完成就会给MQ发送确认,如果没有消费成功就会回滚。
2.不使用事务,使用手动确认 Session.CLIENT_ACKNOWLEDGE,再消息消费完成后手动textMessage.acknowledge();