JMS-ActiveMQ学习-2 基于JMS的消息传送

1.JMS整体设计结构

 

1>基本要素:

生产者:producer

消费者:consumer

消费服务:broker

2>交互模型:

生产者生产消息

消费者消费<处理>消息

broker存储消息

2.JMS两种消息传送模式

1>点对点:(point-to-point 一对一)专门用于使用队列Queue传送消息:发一个消息只有一个消费者能接收到,这个是由消息服务器实现的控制

基于队列Queue的点对点消息只能被一个消费者消费,如多个消费者都注册到同一个消息队列上,当生产者发送一条消息后,而只有其中一个消费者会接收到该消息,而不是所有消费者都能接收到该消息;

2>发布/订阅(publish/subscribe 一对多)专门用于使用主题Topic传送消息:

基于主题的发布与订阅消息能被多个消费者消费,生产者发送的消息,所有订阅了该topic的消费者都能接收到;

3.JMS的API设计

分为3部分:

公共API:可以向一个队列或者主题发送消息或从其中接收消息

点对点API:专门用于使用队列Queue传送消息

发布订阅API:只能实现发布订阅模式的发送

实际代码中使用的是公共API,在JMS公共API内部,和发送与接收消息有关的JMS API接口主要是:

ConnectionFactory

Connection

Session

Message

Destination

MessageProducer

MessageConsumer

一旦有了ConnectionFactory,就可以创建Connection,一旦有了Connection就可以创建Session,一旦有了Session就可以创建Message、MessageProducer和MessageConsumer.

JMS点对点API:

QueueConnectionFactory

QueueConnection

QueueSession

Message

Queue

QueueSender

QueueReceiver

JMS发布订阅API

TopicConnectionFactory

TopicConnection

TopicSession

Message

Topic

TopicPublisher

TopicSubscriber

4.JMS点对点消息发送示例

public class Sender {
//ActiveMQ服务器的连接地址
public static final String BROKER_URL = "tcp://127.0.0.1:61616";
//目的地
public static final String DESTINATION = "myQueue";
public static void main(String[] args) {
MessageProducer messageProducer = null;
Session session = null;
Connection connection = null;
//1.创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
try {
//2.创建连接
connection = connectionFactory.createConnection();
//3.创建session
session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
//4.有了session之后可以做很多事情
//a.创建message
Message message = session.createTextMessage("jms点对点连接");
//b.创建消息目的地-p2p创建queue
Destination destination = session.createQueue(DESTINATION);
//c.创建消息生产者
messageProducer = session.createProducer(destination);
//5.生产者生产消息
messageProducer.send(message);//注意:此send(message)是void没有返回值的
} catch (JMSException e) {
e.printStackTrace();
}finally{
//关闭连接
try {
if(null != messageProducer){
messageProducer.close();
}
if(null != session){
session.close();
}
if(null != connection){
connection.close();
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}

启动activeMQ,发送完消息,打开localhost:8161 ,点击Queue,以下是各个参数的说明:

apache-activemq-5.15.5\data\kahadb  这个目录作为消息的日志,里面四个文件存储处理消息

5.JMS点对点消息接收示例
public class Receiver {
public static final String BROKER_URL = "tcp://localhost:61616";
public static final String DESTINATION = "myQueue";
public static void main(String[] args) {
Connection connection = null;
MessageConsumer messageConsumer = null;
Session session = null;
//1.创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
try {
//2.创建连接
connection = connectionFactory.createConnection();
//3.创建会话
session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
//4.创建session后可创建以下内容
//a.创建目的地
Destination destination = session.createQueue(DESTINATION);
//b.创建消息消费者
messageConsumer = session.createConsumer(destination);
//注意:在接收消息之前一定要启动连接
connection.start();
//5.消息消费者接收消息
Message message = messageConsumer.receive();
if(message != null && message instanceof TextMessage){
String msg = ((TextMessage)message).getText();
System.out.println("已经接收到消息,内容如下:");
System.out.println(msg);
}
} catch (JMSException e) {
e.printStackTrace();
}finally {
try {
if(null != messageConsumer) {
messageConsumer.close();
}
if(null != session){
session.close();
}
if (null != connection){
connection.close();
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
运行结果如下

已经接收到消息,内容如下:
jms点对点连接

此时查看activeMQ队列信息如下:

Number Of Consumers是运行中的消费者的数量 

以上的receive()是一个阻塞方法,main方法再重启的时候停留在receive()了,但是项目存在的问题是当生产者发送多个消息时,消费者只能通过main运行才能接收一个,所以可以通过

1>while(true){。。。} 死循环的方式来接收

6.JMS发布订阅模式消息发送示例
public class TopicPublisher {
//ActiveMQ服务器的连接地址
public static final String BROKER_URL = "tcp://127.0.0.1:61616";
//目的地
public static final String DESTINATION = "myTopic";
public static void main(String[] args) {
MessageProducer messageProducer = null;
Session session = null;
Connection connection = null;
//1.创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
try {
//2.创建连接
connection = connectionFactory.createConnection();
//3.创建session
session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
//4.有了session之后可以做很多事情
//a.创建message
Message message = session.createTextMessage("jmsTopic连接");
//b.创建消息目的地-p2p创建queue
Destination destination = session.createTopic(DESTINATION);
//c.创建消息生产者
messageProducer = session.createProducer(destination);
//5.生产者生产消息
messageProducer.send(message);//注意:此send(message)是void没有返回值的
} catch (JMSException e) {
e.printStackTrace();
}finally{
//关闭连接
try {
if(null != messageProducer){
messageProducer.close();
}
if(null != session){
session.close();
}
if(null != connection){
connection.close();
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
7.JMS发布订阅模式消息接收示例
发布订阅模式必须是订阅者先启动,发布者后启动
public class TopicSubscriber {
public static final String BROKER_URL = "tcp://localhost:61616";
public static final String DESTINATION = "myTopic";
public static void main(String[] args) {
Connection connection = null;
MessageConsumer messageConsumer = null;
Session session = null;
//1.创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
try {
//2.创建连接
connection = connectionFactory.createConnection();
//3.创建会话
session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
//4.创建session后可创建以下内容
//a.创建目的地
Destination destination = session.createTopic(DESTINATION);
//b.创建消息消费者
messageConsumer = session.createConsumer(destination);
//注意:在接收消息之前一定要启动连接
connection.start();
//5.消息消费者接收消息
while(true){
Message message = messageConsumer.receive();
if(message != null && message instanceof TextMessage){
String msg = ((TextMessage)message).getText();
System.out.println("已经接收到消息,内容如下:");
System.out.println(msg);
}
}
} catch (JMSException e) {
e.printStackTrace();
}finally {
try {
if(null != messageConsumer) {
messageConsumer.close();
}
if(null != session){
session.close();
}
if (null != connection){
connection.close();
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
如果订阅者没启动,发布者发布的消息永久不会被消费

 
 
8.Queue模式和Topic模式的比较


 

9.拉模式和推模式
拉模式:点对点消息,如果没有消费者监听队列,消息会保存,不是消息自动推动给消息消费者的,而是由消费者从队列中请求获得
推模式:发布订阅模式,是一个推模式,消息会自动广播,消息会主动推送,但是此消息就推送一遍,若此时监听者没启动,也不会再推送。
10.ActiveMQ的消息类型
1>TextMessage: String类型的消息
2>ObjectMessage:携带一个可以序列化的java对象,用于java对象类型的消息交换
3>MapMessage:映射消息 只能是原始数据类型的限制导致使用比较少
4>BytesMessage:字节消息
5>StreamMessage:流消息

实例1:ObjectMessage类型:两种实现:1>传序列化的对象2>对象转为json格式的TextMessage(好处就是不需要依赖sender中的对象了)
创建Student类并序列化添加到sender和receiver项目中

在sender项目中:
Message objectMessage = session.createObjectMessage(student);
messageProducer.send(objectMessage);
在receiver项目中:
if(message instanceof ObjectMessage){
Student student = (Student)((ObjectMessage) message).getObject();
System.out.println(student);
}
运行:

不受信任的包;那么需要在receiver项目中添加:
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
List<String> trustedPackages = new ArrayList<String>();
trustedPackages.add("com.test.jms.model");
connectionFactory.setTrustedPackages(trustedPackages);
注意:student类路径和serialVersionUID必须是相同的;
当然也可以将对象转换成json格式的文本消息发送,如下:
项目中添加json依赖后
Message message3 = session.createTextMessage(JSONObject.toJSONString(student));

实例2:MapMessage类型
sender:
MapMessage message4 = session.createMapMessage();
message4.setBoolean("boolean",Boolean.FALSE);
message4.setDouble("double",0.1);
messageProducer.send(message4);
receiver:
if(message instanceof MapMessage){
Boolean value1 = ((MapMessage) message).getBoolean("boolean");
Double value2 = ((MapMessage) message).getDouble("double");
System.out.println("value1="+value1+",value2="+value2);
}
实例3:BytesMessage
sender:
BytesMessage message5 = session.createBytesMessage();
message5.writeBoolean(true);
message5.writeInt(1);
message5.writeUTF("保证发送和接收顺序一致");
...
messageProducer.send(message5);
receiver:
if(message instanceof  BytesMessage){
Boolean value1 = ((BytesMessage) message).readBoolean();
int value2 = ((BytesMessage) message).readInt();
String value3 = ((BytesMessage) message).readUTF();
System.out.println("value1="+value1+",value2="+value2+"value3="+value3);
}
实例4:StreamMessage
sender:
StreamMessage streamMessage = session.createStreamMessage();
streamMessage.writeBoolean(false);
streamMessage.writeDouble(0.1);
receiver:
if(message instanceof StreamMessage){
Boolean value1 = ((StreamMessage) message).readBoolean();
Double value2 = ((StreamMessage) message).readDouble();
System.out.println("value1="+value1+",value2="+value2);
}
11.ActiveMQ的消息分为事务消息和非事务消息
事务消息:connection.createSession(true,value);
非事务消息:connection.createSession(false,value);

事务消息必须在发送完和接收完之后调用session.commit();//手动提交事务,非事务消息自动提交 -->发送者发送完成所有的提交,接收者接收完一次就提交

因为正常情况下,一个消息只能被消费一次,例如发送短信,事务消息如果不提交就会反复被消费

12.ActiveMQ的消息确认机制
消息的成功消费包含三个阶段:
1>客户接收消息
2>客户处理消息
3>消息被确认
4种确认机制:

1>自动确认机制:
Session.AUTO_ACKNOWLEDGE : 1,3,2的执行顺序

消费者成功的从receive方法返回的时候,或者从MessageListener.onMessage方法成功返回时,会话自动确认消息

2>客户端<手动>确认机制:
Session.CLIENT_ACKNOWLEDGE 1,2,3的执行顺序或者1,3,2的执行顺序
发送者和接收者的消息确认机制不一样时,以接收者为准,
sender:
发送50个消息
receiver:
session = connection.createSession(Boolean.FALSE,Session.CLIENT_ACKNOWLEDGE);
while(true){
Message message = messageConsumer.receive();
if(message != null && message instanceof TextMessage){
String msg = ((TextMessage)message).getText();
System.out.println("已经接收到消息,内容如下:");
System.out.println(msg);
}
message.acknowledge();//手动确认
}
3>事务确认:
Session.DUPS_OK_ACKNOWLEDGE:
不是必须确认,是一种"懒散的"消息确认,消息可能会重复发送,在第二次重新传送消息时,消息头的JMSRedelivered会被置为true标识当前消息已经传送过一次,客户端需要进行消息的重复处理控制。
该种机制比较难复现。
事务消息和消息确认机制有重叠的地方,如果是事务消息,那么消息的确认实质是看有无session.commit(),消息确认参数都是无效的,建议消息确认机制使用Session.SESSION_TRANSACTED
 
 
13.ActiveMQ持久化消息和非持久化消息
在生成messageProducer之后
不持久化:messageProducer.setDeleveryMode(DeleveryMode.NON_PERSISTENT)
持久化:messageProducer.setDeleveryMode(DeleveryMode.PERSISTENT)
data中4个文件可以观察大小的变化来查看
14.ActiveMQ消息过滤
该机制可以根据消息选择器中的标准来执行消息过滤,只接受符合过滤标准的消息
生产者可在消息中放入特有的标志,而消费者使用基于这些特定的标志来接收消息
1>发送消息放入特殊标志
message中放入特殊消息的标识,例如:message.setStringProperty(name,value)
2>接收消息使用基于特殊标志的选择器
MessageConsumer中定义一个过滤选择器
例如
sender:
for (int i=0;i<50;i++) {
message.setIntProperty("flag",i);
messageProducer.send(message);//注意:此send(message)是void没有返回值的
}
receiver:
String selector  = "flag = 10";//当多个条件的时候以and隔开 flag = 10 and age = 1等
messageConsumer = session.createConsumer(destination,selector);
发现消息只被消费掉了一个,因为1-50只有一个10满足条件,当然除上述外,还可以使用flag > 10各种条件来筛选
15.ActiveMQ消息的接收方式(只针对接收者consumer而言)
同步接收:相当于是只有一个线程在接收,receive()就是同步接收方式,上述项目中没有while(true)只有能接收到一个消息
异步接收:相当于是两个线程在接收,采用监听器MessageListener来接收就是异步接收,线程1在监听,线程2在处理消息onMessage对应的代码,线程1监听到消息后交给线程2进行处理,而且不能关掉连接
同一个consumer中只能采用一种接收方式,项目中大多数采用异步接收的方式
注意:消费者采用异步的方式接收一定不能关掉连接
messageConsumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if(message != null && message instanceof TextMessage){
try {
String msg = ((TextMessage) message).getText();
System.out.println("已经接收到消息,内容如下:");
System.out.println(msg);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
});






























 


 

 

转载于:https://www.cnblogs.com/healthinfo/p/9561102.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值