JMS Java消息服务(Java Message Service)
JMS
在一些场景下RPC
的同步方式可能不太适合业务逻辑的处理,并且这种方式在某些场景下会导致业务的紧耦合
。
基于异步交互模型的JMS
解决了RPC
产生的紧耦合
问题,它提供了一个可以通过网络访问的抽象消息队列。
结构
JMS
应用由以下几个部分组成:
- A JMS provider: A messaging system that implements the JMS specification.
- JMS clients: Java applications that send and receive messages.
- Messages: Objects that are used to communicate information between JMS clients.
- Administered objects: Preconfigured JMS objects that are created by an administrator for the use of JMS clients.
消息传输模型
JMS支持两种消息模型
-
point to point (queuing)
-
publish-subscribe (topic)
消息体组成
在JMS
应用中,一个消息由三部分组成:header
,properties
,body
。
- header(required)
消息头
,必填,包含了路由信息和标识信息。 - properties(optional)
属性
,可选,由key-value
对构成,可以看做是对header
的扩展。 - body(optional)
消息体
,可选,包含真正要传递的数据。JMS规范
定义了JMS Provider
必须要支持的六种消息类型:- Message:没有消息正文的消息。
- StreamMessage:包含java基础类型的流,按顺序读写。
- MapMessage:消息体为键值对,不定义顺序。
- TextMessage:文本消息,消息体为字符串,例如XML消息。
- ObjectMessage:消息体为一个序列化的java对象。
- ByteMessage:字节消息,正文为未解释的字节。
消息的生产和消费-编程模型
点对点队列API
发布-订阅API
消息的生产
-
使用JNDI找到
ConnectionFactory
对象,或者直接实例化一个ConnectionFactory
,最终得到一个QueueConnectionFactory
或者TopicConnectionFactory
的实例,通过这个实例为生产者创建连接。
使用JNDI查找连接工厂对象:Context ctx = new InitialContext(); ConnectionFactory cf1 =(ConnectionFactory)ctx.lookup("jms / QueueConnectionFactory"); ConnectionFactory cf2 = (ConnectionFactory) ctx.lookup("/jms/TopicConnectionFactory");
直接实例化连接工厂:
ConnectionFactory connFactory = new com.sun.messaging.ConnectionFactory();
-
使用
ConnectionFactory
创建连接Connection
。Connection connection = connFactory.createConnection();
注意:调用结束后调用
connection.close()
关闭所有已经创建的连接。 -
使用
Connection
对象创建Session
。这些Session
将一组发送和接收合并到一个原子单元内,并提供事务上下文。Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
createSession()
方法有两个参数:第一个表示session是否使用事务,第二个表示session在成功收到消息后自动确认。 -
使用
JNDI
查找Destination
对象,或者直接实例化Destination
。客户端使用
Destination
对象来指定它消费的消息的来源或者它生产的消息的目标。在point to point消息传递中,Destination
为Queue,在消息传递的发布/订阅模型中,为Topic。
JNDI方式:Destination dest = (Queue) ctx.lookup("jms/SomeQueue");
直接实例化:
Queue q = new com.sun.messaging.Queue("world");
-
通过
Session
和Destination
创建MessageProducer
,MessageProducer
用来发送消息。下面的代码中没有说明Destination
的使用,但是每一个消息必须指定Destination
。MessageProducer producer = session.createProducer(SomeQueue OR SomeTopic);
完成生产者创建之后,就可以使用生产者发送消息
producer.send(message);
消息的消费
1.2.3.4 同Producer
消息的消费分为同步消费和异步消费两种。同步消费是使用receive()
方法,而异步消费则使用消息监听器,MessageListner
。
-
通过
Session
和Destination
创建MessageConsumer
,MessageProducer
用来接收消息。MessageConsumer consumer = session.createConsumer(SomeQueue or SomeTopic);
如果是一个发布/订阅模式的消费者,可以使用
Session.createDurableSubscriber()
创建一个持久的topic订阅者。和
Producer
同样,创建之后可以使用其功能,不同的是MessageConsumer
不是主动模式,而是被动模式。在启动连接之前,消息不会传递,必须先启动连接,才能接收消息。connection.start(); Message msg = consumer.receive();
consumer.receive()
可传入一个long
型参数来指定超时时间,单位是ms
。注意:使用
receive()
方法是同步消费,异步消费需要使用消息监听器。 -
如果需要异步通信,需要实例化
MessageListener
并在MessageConsumer
中注册这个监听器。MessageListener listener = new MyListener(); consumer.setMessageListener(listener);
为了避免丢失消息,注册监听器后,调用连接的
start()
方法,当消息开始传递,JMS会自动调用监听器的onMessage()
接收消息。