目录
1.基本介绍
JMS编码总体架构:
- 通过ConnectionFactory创建一个Connection
- 由Connection创建一个Session
- 由Session创建消息Msg,创建消息的生产者Message Producer和消息消费者Message Consumer
- 消息的生产者将消息发送到Destination,消息的消费者从Destination接收消息
对Destination的说明:
- Destination可以是队列,也可以是主题
- 队列(1对1)
- 队列是将消息发送给一个接收者,如我们微信发消息给一个人
- 主题(1对多)
- 主题是将消息发送给多个订阅它的接收者,如关注了公众号的,公众号都会将自己的消息推送给关注了公众号的所有人
我们来看看传统的JDBC的使用流程:
其实ActiveMQ的使用与JDBC有异曲同工之妙,也有固定的步骤
2.队列代码示例
2.1 生产者实现
队列编码Demo实现
创建Maven工程
导入activeMQ要用的jar包和其他基础jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.cqu</groupId>
<artifactId>activemq_Demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--activemq所依赖的jar包-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.9</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.16</version>
</dependency>
<!--下面是junit/log4j等通用基础配置-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
消息生产者代码实现
package cn.cqu.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProducer {
private static final String ActiveMQ_URL = "tcp://192.168.43.63:61616";
private static final String QUEUE_NAME = "queue01";
public static void main(String[] args) throws JMSException {
//1.创建连接工厂,按照给定的Url地址,采用默认用户名和密码
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQ_URL);
//2.通过连接工厂,获得Connection连接并启动访问(此处为javax.jms.Connection)
Connection connection = connectionFactory.createConnection();
connection.start();
//3.创建会话Session
//两个参数:第一个:事务 第二个:签收
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//4.创建目的地(队列还是主题)
Queue queue = session.createQueue(QUEUE_NAME);
//5.创建消息的生产者
MessageProducer messageProducer = session.createProducer(queue);
//6.通过使用messageProducer生产3条消息发送到MQ的队列当中
for (int i = 0; i < 3; i++) {
//7.通过Session创建Message
TextMessage textMessage = session.createTextMessage("msg----"+i); //可以理解为发送一个字符串给队列
//8.通过messageProducer发送给队列
messageProducer.send(textMessage);
}
//9.关闭资源
messageProducer.close();
session.close();
connection.close();
System.out.println("消息发布完成");
}
}
运行消息生产者
我们可以通过前台管理控制台可以看到消息被成功发送到消息队列中
2.2 消费者的两种方式
(1)同步阻塞方式
消息消费者代码实现的第一种方法:同步阻塞方式
- 订阅者或接收者MessageConsumer的receive()方法来接收消息,receive方法在能够接收到消息之前(超时之前)将一直阻塞,参考如下代码
package cn.cqu.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsConsumer {
private static final String ActiveMQ_URL = "tcp://192.168.43.63:61616";
private static final String QUEUE_NAME = "queue01";
public static void main(String[] args) throws JMSException {
//1.创建连接工厂,按照给定的Url地址,采用默认用户名和密码
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQ_URL);
//2.通过连接工厂,获得Connection连接并启动访问(此处为javax.jms.Connection)
Connection connection = connectionFactory.createConnection();
connection.start();
//3.创建会话Session
//两个参数:第一个:事务 第二个:签收
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//4.创建目的地(队列还是主题)
Queue queue = session.createQueue(QUEUE_NAME);
//5.创建消息的消费者
MessageConsumer messageConsumer = session.createConsumer(queue);
while (true){
TextMessage textMessage = (TextMessage) messageConsumer.receive();
if(textMessage != null){
System.out.println("消费者接收消息:"+textMessage.getText());
}else {
break;
}
}
messageConsumer.close();
session.close();
connection.close();
}
}
因为我上述使用是receive()方法,所以可以看到在没有消息的时候,消费者一直在这里等着,即在receive方法中阻塞
当我手动关闭消费者后:
消费者数量变成了0
而当我们把上述消费者的receive()方法
换成
消费者将会等待4s后自动退出receive方法,不进行阻塞
(2)异步非阻塞方式
消息消费者代码实现的第二种方法:异步非阻塞的方式(监听器onMessage())
- 订阅者(或接收者)通过MessageConsumer的setMessageListener(MessageListener listener)注册一个消息监听器,当消息到达之后,系统自动调用监听器MessageListener的onMessage(Message message)方法,参考代码如下:
package cn.cqu.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumer {
private static final String ActiveMQ_URL = "tcp://192.168.43.63:61616";
private static final String QUEUE_NAME = "queue01";
public static void main(String[] args) throws JMSException, IOException {
//1.创建连接工厂,按照给定的Url地址,采用默认用户名和密码
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQ_URL);
//2.通过连接工厂,获得Connection连接并启动访问(此处为javax.jms.Connection)
Connection connection = connectionFactory.createConnection();
connection.start();
//3.创建会话Session
//两个参数:第一个:事务 第二个:签收
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//4.创建目的地(队列还是主题)
Queue queue = session.createQueue(QUEUE_NAME);
//5.创建消息的消费者
MessageConsumer messageConsumer = session.createConsumer(queue);
//6.设置消息监听者进行监听
messageConsumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
if(null != message && message instanceof TextMessage){
TextMessage textMessage = (TextMessage)message;
try {
System.out.println("消费者接收消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
//等待上述代码执行完,当我们在键盘随便输入一个字符的时候,这个方法执行完
System.in.read();
messageConsumer.close();
session.close();
connection.close();
}
}
2.3 消费者和生产者启动顺序对结果的消费的影响
情况1:当我先启动一个消费者进行消费,再启动一个消费者是否还可以进行消费?
- 结论:1号消费者将消息消费完,2号消费者不会再进行消费
- 先到先得,消息全部给1个
情况2:先启动两个消费者,再启动生产者生产6条消息,消费情况如何?
- 1号消费者先启动进行监听
- 再启动2号消费者进行监听
- 启动生产者生产6条数据
- 这时再看两个消费者
- 结论:当有多个消费者在进行监听的时候,生产者生产消息后会被平均的分配给多个消费者
消息中间件中,生产者和消费者启动的顺序,订阅发布顺序都会影响程序
2.4 总结
JMS开发的基本步骤:
- 1.创建一个connection factory
- 2.通过connection factory来创建JMS connection
- 3.启动JMS connection
- 4.通过connection创建JMS session
- 5.创建JMS destination
- 6.创建JMS producer或者创建JMS message并设置destination
- 7.创建JMS consumer或者是注册一个JMS message listener
- 8.发送或者接收JMS message
- 9.关闭所有的JMS资源
点对点消息传递的特点如下:
- 1.每个消息只能有1个消费者,类似1对1的关系
- 2.消息的生产者和消费之间没有时间上的相关性,无论消费者在生产者发送消息的时候是否处于运行状态,消费者都可以提取消息,
- 3.消息被消费后队列中不会再存储,所以消费者不会消费到已经被消费掉的消息
3.主题代码示例
3.1 生产者
package cn.cqu.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProducer {
private static final String ActiveMQ_URL = "tcp://192.168.43.63:61616";
private static final String TOPIC_NAME = "topic01";
public static void main(String[] args) throws JMSException {
//1.创建连接工厂,按照给定的Url地址,采用默认用户名和密码
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQ_URL);
//2.通过连接工厂,获得Connection连接并启动访问(此处为javax.jms.Connection)
Connection connection = connectionFactory.createConnection();
connection.start();
//3.创建会话Session
//两个参数:第一个:事务 第二个:签收
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//4.创建目的地(队列还是主题)
Topic topic = (Topic) session.createTopic(TOPIC_NAME);
//5.创建消息的生产者
MessageProducer messageProducer = session.createProducer(topic);
//6.通过使用messageProducer生产3条消息发送到MQ的队列当中
for (int i = 0; i < 3; i++) {
//7.通过Session创建Message
TextMessage textMessage = session.createTextMessage("msg----"+i); //可以理解为发送一个字符串给队列
//8.通过messageProducer发送给队列
messageProducer.send(textMessage);
}
//9.关闭资源
messageProducer.close();
session.close();
connection.close();
System.out.println("消息发布完成");
}
}
3.2 消费者
消费者1:
package cn.cqu.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumer01 {
private static final String ActiveMQ_URL = "tcp://192.168.43.63:61616";
private static final String TOPIC_NAME = "topic01";
public static void main(String[] args) throws JMSException, IOException {
System.out.println("我是1号消费者");
//1.创建连接工厂,按照给定的Url地址,采用默认用户名和密码
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQ_URL);
//2.通过连接工厂,获得Connection连接并启动访问(此处为javax.jms.Connection)
Connection connection = connectionFactory.createConnection();
connection.start();
//3.创建会话Session
//两个参数:第一个:事务 第二个:签收
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//4.创建目的地(队列还是主题)
Topic topic = session.createTopic(TOPIC_NAME);
//5.创建消息的消费者
MessageConsumer messageConsumer = session.createConsumer(topic);
messageConsumer.setMessageListener((message)->{
if(null != message && message instanceof TextMessage){
TextMessage textMessage = (TextMessage)message;
try {
System.out.println("消费者接收消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
System.in.read();
messageConsumer.close();
session.close();
connection.close();
}
}
消费者2:
package cn.cqu.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumer02 {
private static final String ActiveMQ_URL = "tcp://192.168.43.63:61616";
private static final String TOPIC_NAME = "topic01";
public static void main(String[] args) throws JMSException, IOException {
System.out.println("我是2号消费者");
//1.创建连接工厂,按照给定的Url地址,采用默认用户名和密码
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQ_URL);
//2.通过连接工厂,获得Connection连接并启动访问(此处为javax.jms.Connection)
Connection connection = connectionFactory.createConnection();
connection.start();
//3.创建会话Session
//两个参数:第一个:事务 第二个:签收
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//4.创建目的地(队列还是主题)
Topic topic = session.createTopic(TOPIC_NAME);
//5.创建消息的消费者
MessageConsumer messageConsumer = session.createConsumer(topic);
messageConsumer.setMessageListener((message)->{
if(null != message && message instanceof TextMessage){
TextMessage textMessage = (TextMessage)message;
try {
System.out.println("消费者接收消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
System.in.read();
messageConsumer.close();
session.close();
connection.close();
}
}
- 先启动两个消费者,再启动上产者
3.3 发布订阅消息传递的特点
- 1.生产者将消息发布到topic中,每个消息可以有多个消费者,属于1:N的关系
- 2.生产者和消费者之间有时间上的相关性,订阅某一主题的消费者只能消费它订阅以后发布的消息。
- 3.生产者生产时,topic不保存消息,它是无状态的,假如无人订阅就去生产,那就是一条废消息,所以一般先启动消费者(订阅者),再启动生产者,不然发送的消息是废消息
JMS规范允许客户创建持久订阅,这在一定程度上放松了时间的相关性要求,持久订阅允许消费者消费它在未处于激活状态时发送的消息,
异常情况:先生产,再订阅
- 生产
- 启动1个消费者
可以发现没有进行消费
- 当我们再次生产
生产的数量与消费的数量不等,多出3条废消息