- 什么是JMS:JMS是javaEE13个规范之一,是通信规范。系统之间进行通信的消息中间件有很多产品,例如ActiveMQ,RocketMQ,Kafka,RabbitMQ等。
- 系统的通信为什么要引入消息中间件:通过消息中间件通信的系统之间可以实现解耦,异步,消锋。
- JMS规范了通信的规则,即架构。例如java与数据库的连接JDBC,也是遵循了JMS编程规范。
- JMS规范原理:遵循JMS规范的系统之间的通信有4个角色:生产者,消费者,消息,目的地(队列或者主题)。
- 目的地Destination有两种类型,对应两种通信模式。分别是点对点模式P2P(即一个生产者对应一个消费者)和发布订阅模式PUB/SUB(一个生产者对应多个消费者)。P2P的Destination为队列Queue,发布订阅模式的Destination为主题Topic。
下面以ActiveMQ为例,介绍如何通过消息中间件(消息队列)实现系统之间的通信。
-
在服务器上安装ActiveMQ,这里安装的是5.16.0版本。
-
启动ActiveMQ,通过url访问ActiveMQ的控制台(后台管理系统)端口是8161(注意区别61616)。(如果访问不通,解决方案参考本机无法url访问服务器原因问题解决-防火墙及服务器控制台设置)
-
创建Maven项目,导入依赖,版本对应
-
P2P模式-生产者
package activemq;
//生产者
//import org.apache.activemq.spring.ActiveMQConnectionFactory; //不是这个包!
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class ActiveMQProducer {
//连接ActiveMQ服务器的url,ActiveMQ默认的服务器连接端口是61616
private static final String ACTIVE_MQ_URL = "tcp://x.x.x.x:61616";
//建立的Destination的名字,这里P2P模式是Queue类型
private static final String DES_QUEUE_NAME = "queue01";
public static void main(String[] args) {
/*
根据JMS架构实现java连接ActiveMQ
*/
//创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ACTIVE_MQ_URL);
try{
//通过连接工厂连接ActiveMQ服务器
Connection connection = connectionFactory.createConnection();
//启动连接
connection.start();
System.out.println("生产者已连接...");
//创建Session
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//创建Destination
Queue queue = session.createQueue(DES_QUEUE_NAME);
//创建消息的发送者
MessageProducer messageProducer = session.createProducer(queue);
//创建消息,假设创建5条消息
for (int i = 0;i < 5;i++){
Message message = session.createTextMessage("消息"+i);
//将消息发送到destination消息队列
messageProducer.send(message);
}
System.out.println("消息已发出...");
//关闭资源
messageProducer.close();
session.close();
connection.close();
System.out.println("消息已发送到MQ...");
}catch (JMSException jmse){
jmse.getErrorCode();
}
}
}
- 启动生产者生产消息投入消息队列,运行结果:
生产者已连接...
消息已发出...
消息已发送到MQ...
- P2P模式-消费者-receive()阻塞模式
package activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class ActiveMQConsumer {
private static final String url = "tcp://x.x.x.x:61616";
private static final String DES_QUEUE_NAME = "queue01";
public static void main(String[] args) {
//创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
try{
//建立连接
Connection connection = connectionFactory.createConnection();
connection.start();
System.out.println("消费者已连接...");
//创建Session
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//消费消息的Destination
Queue queue = session.createQueue(DES_QUEUE_NAME);
//创建消费者
MessageConsumer messageConsumer = session.createConsumer(queue);
//消费消息
while (true){ //表示只要队列中有消息就一直获取,直到队列为空
//如果是receive()表示如果队列为空,消费者一直阻塞,receive(long l)表示当队列为空则阻塞一定时间就返回
//生产消息的类型必须与消费消息的类型一致,receive()是阻塞的
TextMessage msg = (TextMessage)messageConsumer.receive(2000);
if (msg == null){
break;
}
System.out.println("消费了消息:"+msg.getText());
}
//关闭资源
messageConsumer.close();
session.close();
connection.close();
System.out.println("消费者已关闭连接...");
}catch (JMSException jmse){
jmse.getErrorCode();
}
}
}
- 启动消费者消费队列中的消息,运行结果:
消费者已连接...
消费了消息:消息0
消费了消息:消息1
消费了消息:消息2
消费了消息:消息3
消费了消息:消息4
消费者已关闭连接...
- 消费者的while(true)改为如下代码:
//消费消息
// while (true){
// //生产消息的类型必须与消费消息的类型一致
// TextMessage msg = (TextMessage)messageConsumer.receive(2000); //receive()是阻塞的
// if (msg == null){
// break;
// }
// System.out.println("消费了消息:"+msg.getText());
// }
TextMessage msg1 = (TextMessage)messageConsumer.receive(100);
TextMessage msg2 = (TextMessage)messageConsumer.receive(100);
TextMessage msg3 = (TextMessage)messageConsumer.receive(100);
System.out.println("消费了消息:"+msg1.getText());
System.out.println("消费了消息:"+msg2.getText());
System.out.println("消费了消息:"+msg3.getText());
输入:
消费者已连接...
消费了消息:消息0
消费了消息:消息1
消费了消息:消息2
消费者已关闭连接...
消息队列的情况:
P2P-生产者与消费者的工作模式
- 先启动生产者,再启动消费者(while(true)方式),消费者会一次性消费队列中所有消息;
- 先启动多个阻塞的消费者(while(true)方式),消费方式为receive()即消息队列中没有消息就阻塞,再启动生产者,多消费者会轮训的消费消息。
下面先启动2个阻塞的消费者:
开启生产者后,消费者轮训的消费消息:
源码分析,未完待续…