JMS,即Java Message Service,它为Java应用程序提供了一种通用的用于创建、发送、接收以及读取消息的方式;
JMS体系架构
1、 JMS Provider
面向消息中间件的,JMS接口的一个实现。提供者可以是Java平台的JMS实现,也可以是非Java平台的面向消息中间件的适配器;
2、 JMS Client
生产或消费基于消息的Java的应用程序或对象;
3、JMS Producer
创建并发送消息的JMS Client;
4、JMS Consumer
接收消息的JMS Client;
5、JMS Message
包括可以在JMS Client之间传递的数据的对象;
6、JMS Queue
一个容纳那些被发送的等待阅读的消息的区域;
7、JMS Topic
一种支持发送消息给多个订阅者的机制;
JMS消息
JMS消息由以下三部分组成:
1、 消息头 :所有消息的消息头都具体相同的字段,用于JMS Client以及JMS Provider对它们进行区别以及进行消息路由;
下面分别对几个重要的消息头字段及其作用和含义进行说明;
1) JMSDestination
消息发送的目的地(队列或主题);创建消息时可以设置JMSDestination,但是在发送完成时其值会更新为发送方所指定的JMSDestination,也就是说发送前该字段会被忽略;当消息被消费时,该字段的值与在它被发送时被设置的值是相同的;
如下面的例子(文中的例子都是基于Apache Active MQ):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Session session = connection.createSession(
false
, Session.AUTO_ACKNOWLEDGE);
// 创建2个目的地
Destination destination = session.createQueue(
"JMS.DEMO"
);
Destination destination2 = session.createQueue(
"JMS.DEMO2"
);
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage(
"Test Message"
);
// 设置消息的目的地为destination2
message.setJMSDestination(destination2);
// 发送消息
publisher.send(message);
System.out.println(message.getJMSDestination());
|
代码中,通过message.setJMSDestination(destination2);设置了message的JMSDestination消息头属性值,我们再看看其输出结果:
1
|
queue:
//JMS.DEMO
|
通过这个例子可以看出,虽然在发送前设置了消息的目的地,但是发送后消息的目的地被重置了;
2) JMSDeliveryMode
指明消息的传输模式,有两种:
DeliveryMode.PERSISTENT:保证消息仅传一次,JMS Provider服务停止后消息不会丢失;
DeliveryMode.NON_PERSISTENT:消息最多传一次,消息会因JMS Provider停止后丢失;
同JMSDestination一样,在发送前设置的会被忽略;
看下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Session session = connection.createSession(
false
, Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue(
"JMS.DEMO"
);
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 发送PERSISTENT消息
publisher.send(session.createTextMessage(
"PERSISTENT MESSAGE"
));
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
// 发送PERSISTENT消息
publisher.send(session.createTextMessage(
"NON_PERSISTENT MESSAGE"
));
|
例子中分别发送了一条PERSISTENT的消息和一条NON_PERSISTENT的消息;当Active MQ重启后,启动消费端,收到的消息如下:
1
|
PERSISTENT MESSAGE
|
该例子说明,在JMS Provider重启后,NON_PERSISTENT消息丢失了,而PERSISTENT消息能正常被消费者消费;
3) JMSMessageID
由JMS Provider指定的消息的唯一标识符;同上面的字段一样,在发送前设置的会被忽略,在发送完成时,由JMS Provider重置该字段;
4) JMSReplyTo
发送端在发送消息时,可以指定该属性(为一个JMSDestination),表示期望收到客户端的响应;是否响应由消费端决定;
如下面的例子:
发送端:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Session session = connection.createSession(
false
, Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue(
"JMS.DEMO"
);
Destination destination2 = session.createQueue(
"JMS.DEMO3"
);
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage(
"Test Message"
);
message.setJMSReplyTo(destination2);
// 发送消息
publisher.send(message);
|
接收端(可以根据情况决定是否需要回复):
1
2
3
4
5
6
7
8
9
10
|
public
void
onMessage(Message message) {
try
{
System.out.println(
"Receive message: "
+ message);
if
(message.getJMSReplyTo() !=
null
) {
session.createProducer(message.getJMSReplyTo()).send(session.createTextMessage(
"This is a reply to"
+ message.getJMSReplyTo()));
}
}
catch
(Exception e) {
e.printStackTrace();
}
}
|
5) JMSRedelivered
当消费者收到带有JMSRedelivered的消息头时,表明该消息在过去传输过但没有被确认;
JMS Provider必须对该字段进行设置,当为true时即告知消费者该消息是重传的,消费者需要自行处理重复的消息;
6) JMSExpiration
消息的过期时间,其值为当前时间加上存活时间(毫秒);当存活时间设置为0时,该字段的值也被设置为0,表示永不过期;
消费端在一般情况下都不会接收到过期的消息,但JMS Provider并不保证这一点;
下面的例子说明了如何设置消息的过期时间:
1
2
3
4
5
6
7
8
9
10
11
12
|
Session session = connection.createSession(
false
, Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue(
"JMS.DEMO"
);
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage(
"Test Message"
);
// 发送消息
publisher.setTimeToLive(
5000
);
publisher.send(message);
|
7) JMSPriority
消息的优先级,0代表最低优先级,9代表最高优先级;
一般0~4为普通优先级,5~9为加快优先级;
JMS规范里并没有要求JMS Provider严格按这个优先级来实现,但是尽可能实现加快优先级消息的传输在普通消息的前面;
同JMSDestination一样,该字段在发送前被忽略,在发送完成时重置;
2、 消息属性 :除了前面提到的消息头以外,JMS消息还提供了对“属性值对”的支持,以对消息头进行扩展;消息属性主要用于消息选择器(message selector详见下文);
1) 属性名:
属性名必须服务消息选择器的命名规则;
2) 属性值:
可以是基本类型及其对象类型以及Map、List和String;
下面的例子中,消息带HashMap的属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Session session = connection.createSession(
false
, Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue(
"JMS.DEMO"
);
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage(
"Test Message"
);
// 发送消息
message.setObjectProperty(
"myProp"
,
new
HashMap() {
{
this
.put(
"key1"
,
"value1"
);
this
.put(
"key2"
,
"value2"
);
}
});
publisher.send(message);
|
3) 清除属性:
JMS不能清除单个属性,但可以通过Message.clearProperties()方法清除所有消息属性;
3、 消息体 :JMS提供了5种类型的消息体:
1) StreamMessage:消息体是Java流,写入和读出都是顺序的;
2) MapMessage:消息体包含key-value对,key为String,value为基本类型,可以通过迭代器访问;
3) TextMessage:消息体是String;
4) ObjectMessage:消息体是可序列化的Java对象;
5) BytesMessage:消息体是字节数组;
可以通过message.clearBody()来清除消息体;但在消费端,消息体是只读的,针对消息的写操作都会抛出MessageNotWritableException异常