2013年6月12日星期三

activemq in action 第二章:理解面向消息的中间件和JMS(2)

2.3.4 消息选择器(Selectors
很多情况下,JMS客户端订阅了一个目标(Destination)的消息,但是客户端可能仅想消费其中一部分,此时需要对消息进行过滤。可以使用消息的头部或者属性字段对消息进行过滤。JMS客户端工具包提供的选择器(selectors),会通知JMS 提供商(Provider),消费者只想接收包含了特定属性字段的消息。
消息选择器通过对消息头中的属性进行过滤,允许JMS客户端指定哪些消息是其关注的消息。选择器使用SQL92表达式的子集定义。消息选择器使用布尔逻辑,通过对消息的头部属性和属性字段为评判目标进行简单布尔计算。不满足表达式的消息将不被投递到客户端。消息选择器不能对消息的实际负载进行过滤,只能对消息头部的属性和属性字段进行过滤。
过滤器通过字符类型的条件表达式,使用javax.jms.Session进行创建。语法、语义与操作符符合SQL92标准,如下表所示:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)
实例代码:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)
消息发送方:

1 发送方为消息提供了一个String类型的客户属性
2 发送方为消息提供了一个double类型的客户属性

接收方进行过滤:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)

1 选择包含了SYMBOL属性名,并且属性值为AAPL的消息。
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)

1 选择有SYMBOL属性名和PRICE属性名,并且SYMBOL的值为AAPLPRICE的值大于当前价钱的消息。
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)

1 接收包含SYMBOL属性名,并且值为AAPLCSCO,并且包含PRICE属性名,属性值大于当前值,并且没有过期的消息。
2.3.4.1 消息体
JMS定义了六种类型的消息体(或称为实际负载 payload)。
Message – 最基础的消息类型。用来发送没有实际负载的消息,这种消息仅有函数头和属性字段。一般用于一般性的通知。
TextMessage – 实际负载为String类型的消息。多用于传输简单的文本类型或者xml类型的数据。
BytesMessage – 用于发送一个未解析的字节数组。
StreamMessage – 用来发送一串原始的Java类型的数据,这些数据将会按照一定的次序填充和读取。
ObjectMessage – 用于串行化的Java对象作为负载。用于复杂的Java对象,同时也支持Java集合类型。

2.3.5 JMS 
JMS有两种类型的消息,分别是发布/订阅式和点对点式。
2.3.5.1 点对点(PTP Point-to-Point
点对点式的消息传输中目标(Destination)将被认为是一个队列,消息可以同步的或者异步的被发送和接收。队列中的每条消息将仅会投递给一个消费者,并且仅投递一次(once-and-only-once 语义)。这很像发送email一样。消费者可以使用MessageConsumer.receive()同步接收队列中的消息,也可以实现MessageListenerMessageConsumer.setMessageListener()方法异步接收消息。队列将会存储所有投递来的消息,直到消息被投递出去或者消息已经过期。
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)

PTP消息相似,订阅消息可以通过MessageConsumer.receive()同步的从主题(topic)处接收消息,也可以通过实现MessageListenersetMessageListener方法异步接收订阅主题。多个消费者可以注册同一个消息队列接收消息,但是一条PTP消息仅会发给一个消费者。上图中需要注意,一个PTP消息的发送者发送的消息仅会被一个消费者接收,并不是所有注册到这个队列的消费者都会接收到同一消息。JMS提供者保证消息有且仅有一次被投递到注册的消费者。JMS提供商会在众多消费者之间做到一种负载平衡。
2.3.5.2 发布/订阅域(Publish/Subscribe  Domain
发布/订阅使用远程的主题(topic)目的地。发布者将消息发布给远程的主题,订阅者注册接收相应主题的消息。所有的发向主题的消息都会被以一种推的方式(push model)投递给所有的接收者。
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)

PTP消息相似,订阅消息可以通过MessageConsumer.receive()同步的从主题(topic)处接收消息,也可以通过实现MessageListenersetMessageListener方法异步接收订阅主题。如果不显示指明保持(durability)消息,主题消息将不会被保持。可以使用具有生命期(durable)的订阅。使用具有生命期的订阅时,如果有订阅者在消息发布时没有与JMS提供者连接,JMS将会为这种订阅者存储消息,当订阅者再次连接到JMS提供者时,这个订阅者将会收到失去连接期间的所有没过期的消息。生命期订阅允许发送消息时,接收者的非连接状态。
2.3.5.2.1 区分生命期消息(Message Durability)和消息持久化(Message Persistence
生命期消息仅适用于发布/订阅消息域,当客户连接到主题时,他们可以决定是使用有生命期的(durable)还是无生命期的(non-durable)订阅。
Durable Subscription – 当接收有生命期主题订阅的消费者在消息发布时处于不可用状态,JMS提供者将会对订阅的主题进行存储,当消费者状态可用时,会接收到之前订阅的消息。
Non-Durable Subscription  - 在消费者不可用期间发布的所有消息,都将丢失。
消息持久化(persistence)是一个独立任何域的主题。主要用于JMS提供者崩溃时消息的恢复。可以通过JMSDeliveryMode属性设置消息的持久化和非持久化属性。

JMS应用中使用Request/Reply消息
尽管JMS规范没有将Request/Reply消息作为一种消息域进行规范,但是却提供了支持这种域的方式,提供了一些消息头部属性和一组方便实用的类,处理基础的Request-reply消息。Request-reply消息是一种同步的后端和前端模式。即不属于PTP域,也不属于pub/sub域。需要结合使用了头部的JMSReplyToJMSCorrelationID属性和临时目的地。JMSReplyTo明确了反馈消息需要投递的目的地,JMSCorrelationID指定了应答的消息是哪一条。这些头部属性用于将反馈消息与已发送消息相关联。临时目的地是用于在连接期间使用,并且只能被连接创建者使用的。
Request/reply提供使用的类包括QueueRequestorTopicRequestor。这些类提供了request()方法发送请求消息,并且为每一次请求等待反馈消息。
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)

这种类型的通信通常使用JMSReplyTo头部属性,和一个临时队列,接收者将反馈消息发送给这个临时队列,并被发送者接收消费。QueueRequestorTopicRequestor只能解决基础的Request/reply,不能处理复杂的情况。就是说只能处理一条Request对应一条reply,不能处理一条Request对应多个接收者的情况。
2.3.6 管理员对象(Administered Objects
管理员对象包含了配置JMS相关信息,一般由JMS管理员进行创建。管理员对象被客户端使用,用来为客户端隐藏JMS提供商的细节,并且将JMS提供者任务进行抽象化。对于JMS提供者嵌入于JavaEE容器的情况更为常见。JMS规范定义了两种管理员对象:ConnectionFactoryDestination.
2.3.6.1 连接工厂(ConnectionFactory
JMS客户端使用ConnectionFactory对象创建于JMS提供者之间的连接。JMS提供者与客户端之间的连接需要使用TCP socket,连接的花销是比较大的,提倡使用连接池技术减少这种开销。JMS客户端可以使用JMS连接创建javax.jms.Session对象,并且使用这个对象与JMS Provider进行交换。
2.3.6.2 Destination
Destination对象封装了提供者相关的消息发送和接收的地址。尽管DestinationSession创建的,但是它们的生命期与创建Session的连接相匹配(就是说Session终结了,连接没有终结,Destination仍然有效)。
一个连接仅有一个临时目的地。临时目的地的生命期将会与创建它们的连接一样长,并且仅有创建它们的连接才能为他们创建新的消费者。临时目的地用于Request/reply类型消息的发送和接收。
2.3.7 使用JMS API创建JMS应用
创建JMS应用程序的步骤:
1.       获得JMS连接工厂(connection Factory
2.       使用JMS工厂创建JMS连接
3.       启动JMS连接
4.       使用JMS连接创建JMS 会话(Session
5.       获得一个JMS的目的地
6.       创建JMS 发送者(Producer
A. 创建JMS发送者
B. 创建JMS消息,并指明消息发送的目的地
7.       创建JMS消费者(Consumer
A. 创建一个消费者
B. 注册一个消息监听器
8.       发送接收JMS消息
9.       关闭JMS资源(connectionSessionproducerConsumer
使用JMSAPI发送一条消息:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)

activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)


1 使用JNDI初始化运行环境。(更多情况下,我们使用spring等环境)
2 在初始化的上下文环境中通过唯一的工厂名字,寻找JMS连接工厂(一般称这个过程为注入,多用于EJBSpring等开发环境)。
3 使用连接工厂,创建JMS连接
4 调用start()方法开启连接
5 使用连接创建一次会话,这里使用了自动消息确认
6 使用会话创建JMS队列
7 使用会话和目的(指6中创建的队列)创建一个发送者(Producer
8 显示的设置了持久化,默认的JMS发送使用的就是持久化方式。
9 创建了一个简单的文本负载消息。
10 使用发送者发送消息到目的地,并且关闭响应的对象连接。

需要注意的是发送方并不关注JMS消费者是否在另一端等待接收消息。
接收者代码:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)


解释略。

注意JMS应用程序中使用多线程
JMS规范定义了多种可以并行访问的API,但是只用很少一部分对象支持并行访问。ConnectionFactoryConnectionDestination对象需要支持并行访问,SessionMessageProducerMessageConsumer等对象不支持并行访问。这就意味着,像SessionMessageProducerMessageConsumer这些对象不应该在多线程中共享。
2.3.8 消息驱动的实体BeanMessage-Driven BeansMDBs
MDBs出现在EJB2.0规范中,目的是将JMS集成到EJBs中,使得异步消息通信就像使用标准JMS APIs一样方便。通过使用JMS MessageListenerEJB以一种推(push,就是接收端在监听,JMS提供者有消息直接投递给监听进程,另外一种方式是poll,轮询方式,就是接收方不断的向JMS提供者查询,是否有其想要的消息已经到达,这对应了JMS的两种消息交换方式,即同步方式和异步方式)的形式从JMS 提供者接收消息。下面是一段示例代码:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS(2)


需要注意的是上述的MyMessageProcessor类实现了MessageDrivenBeanMessageLstener两个接口。MessageDrivenBean接口需要对setMessageDrivenContext()方法和ejbRemove()方法提供实现。这两个方法都是由EJB容器调用,用来创建和销毁MDBMessageListener接口仅包含了一个称为onMessage()的方法。当新的消息到达了MDB注册的目的地后,JMS提供者会自动的调用onMessage()方法接收消息。
此外EJB容器可以管理所有的必要资源,包括JavaEE的资源(例如:JDBCJMSJCA的连接等)、安全、事务甚至是JMS消息的确认消息。使用MDBs的最大优势在于可以并行的对消息进行处理。典型的JMS客户端需要手动的管理其拥有的资源和环境,并且往往以一种串行的方式来处理消息,即一次处理一个消息(除非在设计之初就考虑将并行处理消息作为重点来处理)。EJB则不同,MDBs可以同时处理尽可能多的消息,这是因为EJB容器可以创建EJB配置文件描述的最大数目的MDB,每个MDB可以处理一个单独的消息。这种配置与具体的JavaEE容器相关。如果您使用Java EE 容器,可以参考帮助文档获取对EJB的配置方法。
MDBs的一个缺陷在于需要一个完整的JavaEE容器。到目前为止支持MDBsEJB容器必须使用完整的Java EE容器。当使用完整的Java EE容器后,MDBs可以被使用,但是,还有可选的方案不必使用完整的JavaEE容器。使用Spring框架JMS相关的APIs使得开发消息驱动的POJOsMDPs)更加容易。就是说,Plain Old Java ObjectsPOJOs)以一种消息驱动的方式被驱动。实际上,这种方式的配置在Java开发领域已经很流行了,因为其避免了完整JavaEE容器所带来的额外代价。
不是所有的EJB容器都需要完整的JavaEE容器支持---OpenEJB
目前为止,基本上所有的EJB容器都是用了完整的JavaEE容器来支持MDBs。只有ApacheOpenEJB不同。OpenEJB通过EJB1.1规范和EJB2规范以及EJB3规范。不论是嵌入式还是独立式的方式支持MDBsOpenEJB可以嵌入到Apache GeronimoJettyApache Tomcat或者是您自己的Java应用程序中,并且支持MDBs
2.4 总结
JMS规范在Java世界中有着巨大的影响,使得面向消息的通信在Java世界中成为可能。这是Java能够在商业领域中满足重要应用集成的关键一步。因为JMS允许以一种标准的形式处理消息。
现在您应该基本了解JMS和其提供的功能,下一步可以学习JMS提供者的实现细节。下一章提供了对Apache ActiveMQ的一些信息。

没有评论:

发表评论