MQ:Message Queue 消息队列
作用: MQ可理解为现实生活中的邮局,当A给物品给B时,A无需直接去找B当面去将物品交给B,A可以通过邮局寄送,然后B通过查询快递信息(监听),知道物品达到邮局,然后B自己去邮局取即可,MQ主要用于实现系统之间的解耦。
工作模式
point-to-point:点对点
特点:点对点并不是只A发送的消息只能指定B接收,而是只A发送的任意一条消息只能由一个人接收处理,也就是每条消息只能被消费一次,类似手机通话。
publish/subscribe
特点:A发送的消息可以被所有监听A的对象的接收,就好比学校的广播,所有的学生都可以收听校园广播信息。
常见MQ产品:ActiveMQ、RabbitMQ、IBMMQ、RocketMQ
ActiveMQ安装
apache官网下载解压,双击运行bin目录下的activemq.bat脚本,默认登陆用户名/密码:admin/admin
ActiveMQ p2p模式: 消息生产者:
package com.zhiwei.advanced.mq.activemq.ptp;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 点对点消息模型
*
* @author Zhiwei Yang
*
*/
public class JMSProducer {
private final static String user = ActiveMQConnection.DEFAULT_USER; // 默认用户名
private final static String password = ActiveMQConnection.DEFAULT_PASSWORD; // 默认密码
private final static String brokeURL = ActiveMQConnection.DEFAULT_BROKER_URL; // 链接地址
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ActiveMQConnectionFactory(JMSProducer.user, JMSProducer.password,
JMSProducer.brokeURL); // 链接工厂
Connection connection = factory.createConnection(); // 连接
connection.start(); // 启动连接
/**
* 参数1:是否支持事务 参数2:消息确认的方式:如果支持事务则忽略
*
* Session.AUTO_ACKNOWLEDGE:当客户成功的从receive方法返回的时候或者从MessageListener.onMessage()
* 方法成功返回时,会话自动确认客户收到的消息
*
* Session.CLIENT_ACKNOWLEDGE:客户通过消息的acknowledge方法确认消息,需要注意这种情况会话
* ,确认是会话层上进行,确认一个被消费的消息将自动确认所有已被会话消费的消息
*
* Session.DUPS_OK_ACKNOWLEDGE:该选择只是会话延迟 确认消息的提交,如果JMS Provider失败
* 可能会导致重复的消息,如果是重复的消息,那么JMS Provider必须把消息头的JMSRedelivered字段 设置为true
*/
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);// 接受或发送消息的线程
Destination destination = session.createQueue("FirstQueue"); // 创建消息队列:Destination子类:Queue/Topic
MessageProducer messageProducer = session.createProducer(destination); // 创建消息生产者
// 发送文本消息
for (int i = 0; i < 10; i++) {
TextMessage message = session.createTextMessage("JMS Provider发送消息:" + i);
System.out.println("JMS Provider发送消息:" + i);
messageProducer.send(message);
}
session.commit(); // 启用事务时session需要提交
session.close();
connection.close();
}
}
消息消费者1
package com.zhiwei.advanced.mq.activemq.ptp;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 点对点消息模型
*
* @author Zhiwei Yang
*
*/
public class JMSConsumer1 {
private final static String user = ActiveMQConnection.DEFAULT_USER; // 默认用户名
private final static String password = ActiveMQConnection.DEFAULT_PASSWORD; // 默认密码
private final static String brokeURL = ActiveMQConnection.DEFAULT_BROKER_URL; // 链接地址
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ActiveMQConnectionFactory(user, password,brokeURL); // 链接工厂
Connection connection = factory.createConnection(); // 连接
connection.start(); // 启动连接
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 接受或发送消息的线程:消费不需事务
Destination destination = session.createQueue("FirstQueue"); // 创建连接消息队列:Destination子类:Queue/Topic
MessageConsumer messageConsumer = session.createConsumer(destination); // 创建消息生产者
//不间断的接受消息
while(true){
TextMessage message = (TextMessage) messageConsumer.receive(10000); //间隔1000ms接受消息
if(message != null){
System.out.println("收到消息" + message.getText());
}else{
System.out.println("消息队列无消息.......");
break;
}
}
session.close();
connection.close();
}
}
消息消费者2:使用监听器方式监听消息队列
package com.zhiwei.advanced.mq.activemq.ptp;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 点对点消息模型
*
* 监听器监听:推荐
*/
public class JMSConsumer2 {
private final static String user = ActiveMQConnection.DEFAULT_USER; // 默认用户名
private final static String password = ActiveMQConnection.DEFAULT_PASSWORD; // 默认密码
private final static String brokeURL = ActiveMQConnection.DEFAULT_BROKER_URL; // 链接地址
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ActiveMQConnectionFactory(JMSConsumer2.user, JMSConsumer2.password,JMSConsumer2.brokeURL); // 链接工厂
Connection connection = factory.createConnection(); // 连接
connection.start(); // 启动连接
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 接受或发送消息的线程:消费不需事务
Destination destination = session.createQueue("FirstQueue"); // 创建连接消息队列:Destination子类:Queue/Topic
MessageConsumer messageConsumer = session.createConsumer(destination); // 创建消息生产者
messageConsumer.setMessageListener(new JMSListener()); //注册消息监听 :阻塞监听
}
}
消费者2监听器
package com.zhiwei.advanced.mq.activemq.ptp;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class JMSListener implements MessageListener{
@Override
public void onMessage(Message message) {
if(message instanceof TextMessage){
try {
System.out.println(((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
测试:
- 先运行provider,ActiveMQ管理员页面显示消息队列FirstQueue,并且收到10条消息
- 运行2个consumer消费消息:10条消息被消费
控制台日志:
结果说明:2个消费者各消费5条信息
注意:activemq中显示的消费者数量是指活跃的消费者,因为其中一个消费者消费完后线程推出,导致WEBUI只显示1个消费者