一、JMS简介
- JMS全称 Java Message Service(Java消息服务),是java的一套API标准,是企业消息产品(有时也被称为消息中间件产品),最初的目的是为了使程序能够访问MOM系统(Message Oriented Middleware 面向消息的中间件 ),利用高效可靠的消息传递机制进行平台无关的数据交流,还可以在分布式系统中发送消息,进行异步通信。JMS是一个于平台无关的API,大多数的MOM提供商都支持JMS。常见的MOM系统:RocketMQ、RabbitMQ、ActiveMQ等,基于JMS实现的MOM,又称为JMS Provider。
- 消息是JMS中提供的一种类型对象,可以在计算机之间进行传输的数据单位,消息类型:简单文本(TextMessage)、可序列化的对象 (ObjectMessage)、属性集合 (MapMessage)、字节流 (BytesMessage)、原始值流 (StreamMessage),还有无有效负载的消息 (Message)。
- 消息被发送到消息队列中,消息队列是消息在传输过程中消息的存放容器,进行异步处理,减少响应时间和解耦。消息队列提供路由保证消息的传递,如果消息没有被消费者消费(被接收),消息队列就会保留消息,直至消息被接收。
二、ActiveMQ简介
ActiveMQ
是由·Apache·出品,完全支持JMS1.1
和JavaEE1.4
规范的JMS Provider
实现,是MOM
(面向消息的中间件)的中间件。
- 1、支持的编程语言:C、C++、C#、Delphi、Erlang、Adobe Flash、Haskell、Java、JavaScript、Perl、PHP、Pike、Python和Ruby;
- 2、支持的协议:OpenWire、REST、STOMP、WS-Notification、MQTT、XMPP以及AMQP;
- 3、支持多种传送协议:in-VM、TCP、SSL、NIO、UDP、IGroups、JXTA。
- 4、对Spring支持,ActiveMQ可以跟Spring整合;
- 5、支持集群,客户端-服务器,点对点;
- 6、支持Ajax。
三、Linux上安装ActiveMQ
首先到官网http://activemq.apache.org/components/classic/download/上进行下载
- 1、将下载好的压缩文件上传到Linux上(我上传的目录是/opt下)
- 2、使用解压命令 tar -zxvf apache-activemq-5.15.9-bin.tar.gz
- 3、检查是否安装JDK,如果没有安装JDK,无法启动ActiveMQ。
- 4、解压完成后进入bin目录 cd apache-activemq-5.15.9/bin ,使用 ./activemq start 命令启动activemq
- 5、启动完成后再浏览器中输入http://你的Linux ip:8161
然后点击红框中的内容,输入用户名和密码(都是 admin)
四、PTP处理模式(Queue)
消息生产者将消息发送到queue(消息队列)中,消息消费者然后从queue中获取消息并消费。消息被消费后,queue中不再有消息,所以消息消费者是不可能去消费已经被消费掉的消息。Queue支持存在多个消息消费者,但是对一个消息而言,只能有一个消息消费者去消费它,其它消息消费者则不能去消费。当消息消费者不存在时,消息就会一直存在queue中,直到有消息消费者去消费消息。
1、主动模式
创建一个JavaSE工程,引入ActiveMQ的jar包。
1)消息生产者
/**
* 消息生产者
*/
public class Producer {
/**
* 发送消息到ActiveMQ中,使用的接口全都在javax.jms包下
*/
public void sendTextMessage(String datas) {
// 连接工厂
ConnectionFactory connectionFactory = null;
// 连接
Connection connection = null;
// 目的地
Destination destination = null;
// 会话
Session session = null;
// 消息生产者
MessageProducer messageProducer = null;
// 消息对象
Message message = null;
try {
/**
* 创建连接工厂对象
* 无参构造:有默认的连接地址(本地连接localhost)
* 单个参数构造:无认证模式(没有用户进行认证),指定连接地址
* 三个参数构造:有认证、指定地址(默认端口是61616,在conf/activemq.xml文件中查看)
*
*
* 目前首先使用ActiveMQ提供的默认用户
*
*/
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"tcp://xxx:61616");
/**
* 通过工厂对象创建连接
* 创建连接方法有两个重载,有参数和无参数
* createConnection(String username, String password),参数是用户名和密码
* 前面创建连接工厂的时候已经传递,这里就不需要了。
*/
connection = connectionFactory.createConnection();
/**
* 消息生产者不是必须启动连接(建议启动),消息消费者必须启动连接
*/
connection.start();
/**
* 通过连接对象创建会话
*
* createSession方法有连个参数,
* 第一个参数:是否支持事务(transacted),true支持、false不支持(常用)
* 第二个参数:acknowledge(如何处理消息,消息处理后就从消息队列中移除),有三个可以值,如下:
* 1、AUTO_ACKNOWLEDGE:自动确认消息(消息消费者处理消息后自动确认,常用)
* 2、CLIENT_ACKNOWLEDGE:客户端手动确认(消息消费者处理消息后手动确认)
* 3、DUPS_OK_ACKNOWLEDGE:允许副本确认,消息可以进行重复确认,但是其他的消息消费者不能再消费这个消息
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 通过会话对象创建目的地,参数是目的地名称,是目的地唯一标识
destination = session.createQueue("ptp auto mq");
/**
* 通过会话对象创建消息生产者Producer,
* 创建Producer的时候可以不指定destination,发送消息的时候在指定
*/
messageProducer = session.createProducer(null);
// 通过会话对象创建文本对象
message = session.createTextMessage(datas);
// 使用Producer对象,发送消息到ActiveMQ中的目的地,如果消息发送失败就抛异常
messageProducer.send(destination, message);
System.out.println("消息发送成功!");
} catch (JMSException e) {
e.printStackTrace();
} finally {
// 释放资源
if (messageProducer != null) {
try {
messageProducer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Producer producer = new Producer();
producer.sendTextMessage("这是第一个ActiveMQ示例");
}
}
2)、消息消费者
/**
* 消息消费者
*/
public class Consumer {
public void receiveTextMessage() {
// 连接工厂
ConnectionFactory connectionFactory = null;
// 连接
Connection connection = null;
// 会话
Session session = null;
// 目的地
Destination destination = null;
// 消息消费者
MessageConsumer consumer = null;
// 消息
Message message = null;
try {
// 创建连接工厂对象
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"tcp://192.168.48.129:61616"
);
// 通过连接工厂对象创建连接对象
connection = connectionFactory.createConnection();
// 消息消费者必须启动连接,否则无法处理消息
connection.start();
// 通过连接对象创建会话
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 通过会话对象创建目的地,参数是目的地名称,是目的地唯一标识
destination = session.createQueue("ptp auto mq");
//通过会话对象创建消息消费者,指定目的地
consumer = session.createConsumer(destination);
// 通过消息消费者对象获取消息队列中的消息
message = consumer.receive();
getMessageType(message);
} catch (JMSException e) {
e.printStackTrace();
} finally {
// 释放资源
if (consumer != null) {
try {
consumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
// 判断消息类型
private void getMessageType(Message message) {
try {
if (message instanceof TextMessage) {
// 强转
TextMessage textMessage = (TextMessage) message;
System.out.println(textMessage.getText());
} else if (message instanceof ObjectMessage) {
ObjectMessage objectMessage = (ObjectMessage) message;
} else if (message instanceof MapMessage) {
MapMessage mapMessage = (MapMessage) message;
} else if (message instanceof BytesMessage) {
BytesMessage bytesMessage = (BytesMessage) message;
} else if (message instanceof StreamMessage) {
StreamMessage streamMessage = (StreamMessage) message;
}
} catch (JMSException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.receiveTextMessage();
}
}
2、监听器消费模式
1)消息生产者
try {
// 创建连接工厂对象
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"tcp://xxx:61616"
);
// 通过连接工厂对象创建连接对象
connection = connectionFactory.createConnection();
// 开启连接
connection.start();
// 通过连接对象创建会话
// 客户端确认
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
// 通过会话对象创建目的地
destination = session.createQueue("test-listener");
/**
* 通过会话对象创建消息生产者Producer,
* 创建Producer的时候可以不指定destination,发送消息的时候在指定
*/
producer = session.createProducer(destination);
// 创建消息对象并发送消息
for (int i = 0; i < 100; i++) {
message = session.createTextMessage("消息" + i);
producer.send(message);
}
} catch (JMSException e) {
e.printStackTrace();
}
2)消息消费者
try {
// 创建连接工厂对象
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"tcp://xxx:61616"
);
// 通过连接工厂对象创建连接
connection = connectionFactory.createConnection();
// 开启连接
connection.start();
// 通过连接对象创建会话
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
// 通过会话对象创建目的地
destination = session.createQueue("test-listener");
// 通过会话对象创建消费者
consumer = session.createConsumer(destination);
// 注册监听器
consumer.setMessageListener(new MessageListener() {
/**
* 监听器一旦注册,永久有效(consumer线程不关闭),
* 处理消息方式:只要有未处理的消息,自动调用onMessage方法处理消息,
* 监听器可以注册若干个,注册多个监听器相当于集群,
* ActiveMQ自动循环调用多个监听器来处理队列中的消息,实现并行处理
*
* @param message 未处理的消息
*/
@Override
public void onMessage(Message message) {
// 使用客户端确认,就要调用acknowledge方法,acknowledge方法就是确认方法,代表消息消费者消费了消息,确认后将对应的消息从消息列表中删除
message.acknowledge();
handleMessage(message);
}
});
} catch (JMSException e) {
e.printStackTrace();
}
}
五、Publish/Subscribe模式(Topic)
消息生产者(发布)将消息发布到topic中,同时多个消息消费者(订阅)消费该消息。
Publish/Subscribe模式和PTP模式不同,发布到topic的消息会被所有的消息消费者(订阅)消费。消息生产者发布消息,不管是否有消息消费者(订阅)消费消息,消息都不会保存。
消息生产者
try {
// 创建连接工厂
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"tcp://xxx:61616"
);
// 通过连接工厂对象创建连接
connection = connectionFactory.createConnection();
// 开启连接(消息生产者建议开启)
connection.start();
//通过连接对象创建会话
// 不开启事务,客户端确认
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
// 通过会话对象创建Topic
destination = session.createTopic("test-topic");
// 通过会话对象创建消息生产者
producer = session.createProducer(destination);
message = session.createTextMessage("这是发布的消息3");
producer.send(message);
System.out.println("发布成功!");
} catch (JMSException e) {
e.printStackTrace();
}
消息消费者
try {
// 创建连接工厂对象
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"tcp://xxx:61616"
);
// 通过连接工厂对象创建连接
connection = connectionFactory.createConnection();
// 开启连接(消息消费者必须开启)
connection.start();
// 通过连接对象创建会话
// 不开启事务,客户端确认
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
// 通过会话对象创建Topic
destination = session.createTopic("test-topic");
// 通过会话对象创建消息消费者
consumer = session.createConsumer(destination);
// 接收消息
message = consumer.receive();
handleMessage(message);
// 使用客户端确认,就要调用acknowledge方法,acknowledge方法就是确认方法,代表消息消费者消费了消息,确认后将对应的消息从消息列表中删除
message.acknowledge();
} catch (JMSException e) {
e.printStackTrace();
}
公众号二维码