一、jms简介
java消息服务(java message service),简称JMS,相对于RMI、Hessian、Burlap等而言,它是面向异步消息而制定的API,实现的消息机制如下图:
在JMS中有两个重要概念:消息代理(message broker)和目的地(destination)。
消息代理:当一个应用发送消息的时候,给把消息交给一个消息代理,消息代理可以确保把消息投放到目的地,同时释放发送者,使其能够继续进行其它业务。
目的地:分为两种类型,队列和主题,每种类型都与特定的消息类型相关联,分别是应用于队列的点对点模型和应用于主题的订阅/发布模型。
点对点消息模型
每一个消息都只有一个发送者和接收者,当消息代理接收到消息时,它将消息放入队列中,当接收者请求队列的下一条消息时,消息会从队列取出,投递给接收者。消息从队列中投递之后会被删除,所以可以保证消息只投递给一个接收者。
如图所示:
虽然每个消息只会投递给一个接收者,但是并不意味着只能使用一个接收者从队列中获取消息。通常可以使用几个接收者共同来处理队列中的消息,每个接收者处理自己接收到的信息。即使用多个接收者监听队列,提高应用的消息处理能力。
发布-订阅模型
消息会发送给一个主题,与队列类似,多个接收者可以同时监听一个主题。但与队列不同的是,所有订阅主题的订阅者都可以接收到此消息。
如图所示:
二、使用ActiveMQ消息代理,实现JMS服务
1、下载ActiveMQ
去官方网站下载:http://activemq.apache.org/
2、运行ActiveMQ
解压缩apache-activemq-5.14.1-bin.zip,得到apache-activemq-5.14.1文件夹,然后去bin双击activemq.bat运行ActiveMQ程序。注意区分win32位还是win64位,选择执行对应文件夹下的activemq.bat
启动ActiveMQ以后,访问 http://localhost:8161/,用户名和密码默认都是admin,看到的就是消息代理activeMQ管理后台。
3、代码实现,项目引入activemq-all-5.14.1.jar依赖包,位于解压缩文件根目录下
基类,声明代理连接工厂和目的地(队列和主题)
/**
* @Description: ActiveMQ消息代理简单使用
*/
public class ActiveMQ {
//默认的用户名、密码、uri:tcp://localhost:61616/
private final String DEFAULT_USERNAME = ActiveMQConnectionFactory.DEFAULT_USER;
private final String DEFAULT_PASSWORD = ActiveMQConnectionFactory.DEFAULT_PASSWORD;
private final String DEFAULT_URL = ActiveMQConnectionFactory.DEFAULT_BROKER_URL;
//消息代理连接工厂
protected ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(DEFAULT_USERNAME, DEFAULT_PASSWORD, DEFAULT_URL);
//目的地:队列
protected Destination queue = new ActiveMQQueue("jsun.queue");
//目的地:主题
protected Destination topic = new ActiveMQTopic("jsun.topic");
}
点对点消息模型
发送者:
/**
* @Description: 发送者
*/
public class Produce extends ActiveMQ{
/**
* @Description :向队列发送消息
*/
public void produce(){
Connection connection = null;
Session session = null;
try {
//获取一个连接
connection = connectionFactory.createConnection();
//启动连接
connection.start();
/*
* 创建session会话
* 第一个参数:是否支持事务,如果为true,则会忽略第二个参数,被jms服务器设置为SESSION_TRANSACTED
* 第一个参数为false时,第二个参数的值可为Session.AUTO_ACKNOWLEDGE,Session.CLIENT_ACKNOWLEDGE,DUPS_OK_ACKNOWLEDGE其中一个。
* Session.AUTO_ACKNOWLEDGE为自动确认,客户端发送和接收消息不需要做额外的工作。哪怕是接收端发生异常,也会被当作正常发送成功。
* Session.CLIENT_ACKNOWLEDGE为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会当作发送成功,并删除消息。
* Session.DUPS_OK_ACKNOWLEDGE允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;而且允许重复确认。
*/
session = connection.createSession(true, Session.SESSION_TRANSACTED);
//由会话创建发送者,指向目的地队列
MessageProducer producer = session.createProducer(queue);
/*
* 设置生产者持久化的模式,有两种可选
* DeliveryMode.PERSISTENT 当activemq关闭的时候,队列数据将会被保存
* DeliveryMode.NON_PERSISTENT 当activemq关闭的时候,队列里面的数据将会被清空,该设置为默认设置
*/
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
for(int i =0;i<10;i++){
System.out.println("向队列发送消息"+i);
//创建消息
TextMessage message = session.createTextMessage();
message.setText("向队列发送消息"+i);
//发送者发送消息
producer.send(message);
}
//事务性session,需要提交事务
session.commit();
} catch (JMSException e) {
e.printStackTrace();
}finally{
try {
//关闭会话和连接
if(session != null){
session.close();
}
if(connection != null){
connection.close();
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
接收者:
/**
* @Description:接收者
*/
public class Receive extends ActiveMQ{
/**
* @Description :从队列接收消息
*/
public void receive(){
Connection connection = null;
Session session = null;
try {
//获取一个连接
connection = connectionFactory.createConnection();
//启动连接
connection.start();
/*
* 创建session会话:使用客户端确认方式,可以保证业务处理完毕之后,消息才从队列中删除
*
* 第一个参数:是否支持事务,如果为true,则会忽略第二个参数,被jms服务器设置为SESSION_TRANSACTED
* 第一个参数为false时,第二个参数的值可为Session.AUTO_ACKNOWLEDGE,Session.CLIENT_ACKNOWLEDGE,DUPS_OK_ACKNOWLEDGE其中一个。
* Session.AUTO_ACKNOWLEDGE为自动确认,客户端发送和接收消息不需要做额外的工作。哪怕是接收端发生异常,也会被当作正常发送成功。
* Session.CLIENT_ACKNOWLEDGE为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会当作发送成功,并删除消息。
* Session.DUPS_OK_ACKNOWLEDGE允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;而且允许重复确认。
*/
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
//由当前会话创建接收者,指向目的地队列
MessageConsumer consumer = session.createConsumer(queue);
while (true) {
//通过订阅者接收消息
TextMessage message = (TextMessage) consumer.receive(2000);
if (null != message) {
//业务处理
System.out.println("从队列收到消息:" + message.getText());
//客户端确认接收到消息,消息从队列中删除
message.acknowledge();
} else {
System.out.println("当前未能从队列收到任何消息。");
//跳出循环,注释之后,让当前订阅者具有持续监控该队列的能力
//break;
}
}
} catch (JMSException e) {
e.printStackTrace();
}finally{
try {
//关闭会话和连接
if(session != null){
session.close();
}
if(connection != null){
connection.close();
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
测试类:
public class ProduceAndReceiveTest {
public static void main(String[] args) throws JMSException {
//发送消息
new Produce().produce();
//接收消息
new Receive().receive();
}
}
发布-订阅模型
发布者:
/**
* @Description: 发布者
*/
public class Publish extends ActiveMQ{
/**
* @Description :订阅发布模式:发布者
*/
public void publish(){
Connection connection = null;
Session session = null;
try {
//获取一个连接
connection = connectionFactory.createConnection();
//启动连接
connection.start();
/*
* 创建session会话
* 第一个参数:是否支持事务,如果为true,则会忽略第二个参数,被jms服务器设置为SESSION_TRANSACTED
* 第一个参数为false时,第二个参数的值可为Session.AUTO_ACKNOWLEDGE,Session.CLIENT_ACKNOWLEDGE,DUPS_OK_ACKNOWLEDGE其中一个。
* Session.AUTO_ACKNOWLEDGE为自动确认,客户端发送和接收消息不需要做额外的工作。哪怕是接收端发生异常,也会被当作正常发送成功。
* Session.CLIENT_ACKNOWLEDGE为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会当作发送成功,并删除消息。
* Session.DUPS_OK_ACKNOWLEDGE允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;而且允许重复确认。
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//由会话创建发布者,指向目的地主题
MessageProducer producer = session.createProducer(topic);
/*
* 设置生产者持久化的模式,有两种可选
* DeliveryMode.PERSISTENT 当activemq关闭的时候,队列数据将会被保存
* DeliveryMode.NON_PERSISTENT 当activemq关闭的时候,队列里面的数据将会被清空,该设置为默认设置
*/
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
for(int i =0;i<10;i++){
System.out.println("向主题发送消息"+i);
//创建消息
TextMessage message = session.createTextMessage();
message.setText("向主题发送消息"+i);
//发送者发送消息
producer.send(message);
}
//事务性session,需要提交事务
//session.commit();
} catch (JMSException e) {
e.printStackTrace();
}finally{
try {
//关闭会话和连接
if(session != null){
session.close();
}
if(connection != null){
connection.close();
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
订阅者:
/**
* @Description:订阅者
*/
public class Subscribe extends ActiveMQ{
/**
* @Description :订阅发布模式:订阅者
*/
public void subscribe(final String name){
Connection connection = null;
Session session = null;
try {
//获取一个连接
connection = connectionFactory.createConnection();
//启动连接
connection.start();
/*
* 创建session会话:使用客户端确认方式,可以保证业务处理完毕之后,消息才从队列中删除
*
* 第一个参数:是否支持事务,如果为true,则会忽略第二个参数,被jms服务器设置为SESSION_TRANSACTED
* 第一个参数为false时,第二个参数的值可为Session.AUTO_ACKNOWLEDGE,Session.CLIENT_ACKNOWLEDGE,DUPS_OK_ACKNOWLEDGE其中一个。
* Session.AUTO_ACKNOWLEDGE为自动确认,客户端发送和接收消息不需要做额外的工作。哪怕是接收端发生异常,也会被当作正常发送成功。
* Session.CLIENT_ACKNOWLEDGE为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会当作发送成功,并删除消息。
* Session.DUPS_OK_ACKNOWLEDGE允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;而且允许重复确认。
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//由当前会话创建订阅者,指向目的地主题
MessageConsumer consumer = session.createConsumer(topic);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message paramMessage) {
TextMessage message = (TextMessage)paramMessage;
try {
System.out.println("我是消费者:"+name+",我接收到的消息为:" + message.getText());
}
catch (JMSException e) {
e.printStackTrace();
}
}
});
} catch (JMSException e) {
e.printStackTrace();
try {
//关闭会话和连接
if(session != null){
session.close();
}
if(connection != null){
connection.close();
}
} catch (JMSException e1) {
e1.printStackTrace();
}
}
}
}
测试类:
public class PublishTest {
public static void main(String[] args) {
//发布
new Publish().publish();
}
}
public class SubscribeTest {
public static void main(String[] args) {
//订阅者
new Subscribe().subscribe("小黑嘿嘿");
new Subscribe().subscribe("======咻咻=======");
new Subscribe().subscribe("9999999999哈哈哈哈哈哈哈999999999");
}
}
总结:
1、点对点的队列模型,消息只能被消费一次;但只要消息被投递到队列,消费者就一定会消费消息,也就是说生产者与消费者启动顺序没有要求。
2、发布-订阅的主题模型,消息可以被多次消费;但订阅者要先于生产者启动,即先订阅后生产,这一定与redis的订阅发布是一样的。