JMS 2是Java EE 7的一个重要“卖点”(如图所示), 本文的重点也落在了JMS 2规范上,通过近4个小时的阅读(156 Page),结合最近Common MQ的实施经验,谈谈对新规范的一些解读,后续会陆续发表一些相关博文,希望大家能够感兴趣。
浏览一下Architecture章节目录,大家很快就会意识到,JMS 2的一个最重要的设计初衷,通过减少编码提高生产力;通过更高层次的抽象,简化消息模型。3,4,5,6,7,8,9章节则陆续在消息模型,消息领域,消息发送,消息接收,异常体系,特性工具集等方面进行详细阐述。第10,11,12章则重点阐述了JMS broker层面的一些内在工具API,如何与Java EE应用集成等(包括JTA事务,MDB,Context Injection)。接下来的章节就是Programmer最希望看到的 - 代码实例荟萃。最后一个章节(也是我这篇文章的开篇),则汇集了JMS version 2的change history。
从这25条Change List,我们可以归纳出JMS 2.0的几个亮点(概括起来,就是Simplify,Clarify,Rectify)
(1)Session创建API的简化,避免了在事务消息下,开发者care消息的AcknowledgeMode。
(2)新增持久消费者创建API,替换JMS1.1里面的Domain-specific的API
(3)剔除同一Topic下允许并发消费者消费限制,带来了更好的可扩展性
(4)延迟投递,这个特性无疑对于本身就具有异步消费语意的消息系统进一个扩展。
(5)消息异步发送,通过回调函数的形式
(6)兼容Java SE 7的try-with-resource语法,简化对像的close操作,通过继承Autocloseable
(7)JMS provider必须设置JMSXDeliveryCount,别小看这个属性,对Session
acknowledge mode,reliable produce,Qos方面都有不小的作用
(8)废除了Message接口中的一些headers设置方法
(9)JMS provider 必须实现PTP,pub/sub两种消息通讯模型
(10) 修正了在使用MapMessage SetBytes方法时name为null的不一致异常表现。
其它的小改进,比方说:订阅名字的128个字符限制,可选择的共享持久化订阅ClientId,Message新增 getBody方法,JMSExpiratoin的定义更加清晰等大家可以查阅手册。
通过上面的概述,我们发现,2.0规范仍旧没有在Load balance/Fault-tolerance,Administration,Securiy,Wire Protocol,Message Store等方面给予规范性指导。这些也成为开源MOM在这些领域百花齐放的一个重要原因,比方说ZeroMQ在Wire Protocol层面的努力,AMQ在Message Store方面的不断尝试,Hornet MQ在Load Balance方面则引入了JGroup。可谓"因为规范,所以简单。因为开源,所以精彩“。
有了上面粗线条的介绍,我们来具体看下规范究竟为我们展示了什么?
首先,我们来看下JMS Application的组成:
这里重点说明一下其中的三个组件:
Non-JMS Clients ,作为JMS clients的一个重要补充,主要使用消息系统的一些Native API做事,比方说在zeroMQ中,如果使用Java版的client(JZMQ),本地可能会有个.o或者.dll的代码库,client通过通过调用native API 可以获得更好的性能(具体可以参见:https://github.com/zeromq/jzmq)。
Messages,在JMS规范中,定义了5中类型:BytesMessage,MapMessage,ObjectMessage,StreamMessage,TextMessage.
Administered Object,在JMS规范中,主要指的是预定义的ConnectionFactory,Destination类(一个用来管理Conenction,一个用来统一message-domain中的queue & topic),在JMS 2的几个核心概念中,也只有它们是支持并发的,这点请大家注意(JMSContext,JMSProducer,JMSConsumer则不然)。
Ok,有了这些知识,我们来看看JMS 2是如何简化API的,上图看真相:
JMS 1.x
JMS 2.x
怎么样,很清晰吧,虽然不喜欢特性化的JMS命名,没办法,老接口还得用,命名只能这样将就一下了,难不成,各位亲还有其它更好的命名?
JMSContext虽然从结构上看,统一了Connection& Session,但是其并未暴露获取它们的API,瞧,封装的多好?(好在哪,请大家思考)不过大家要明白Connection对象代表了一条到JMS broker的物理链接抽象,而Session则代表了单线程的会话(Send or Receive)
下面,我们再来看另一个设计细节:JMS message的组成:
Header:
Properties(Application-specific properties & Standard properties,Provider-specific properties)
Application-specific properties &Standard properties:
Provider-specific properties : 如含有JMS_<vendor_name>属性名前缀,主要用在provider-native clients当中。
Message Selector,再来看下这个存在已久的特性,它支持SQL 92语法,可以通过消息头,消息属性过滤实际投递到消费端消息,通过这个定义,我们似乎觉得Message Filter的概念更为贴近它的使用场景,有么有???
Message Order,首先来看Order of message receipt,规范上写的挺清楚的,不解释
JMS defines that messages sent by a session to a destination must be received in the order in which they weresent . This defines a partial ordering
constraint on a session’s input messagestream.
JMS does not define order of message receipt across destinations or across a destination’s messages sent from multiplesessions. This aspect of a
session’s input message stream order istiming-dependent. It is not under application control.
再来看Order of message sends,这里面有几个影响因子:Message Priority,Delivery mode。一句话概括一下:不保证,JMS provider自己实现。
在这一点上LinkedIn的Kafka通过Partition等有条件地实现了顺序消息,不过也从一定程度上牺牲了MOM的可扩展性,吞吐量。
最后一个主题:Duplicate production of message & Duplicate delivery of message,规范里的硬性规定其实很直白: 不能投递已经确认了(acknowledged message)的信息两次(second copy); 不能够生产duplicate message,由于session recovery导致的情况除外。哈哈,剩下的就由JMS provider 去实现吧(AT_MOST_ONCE,DUPLICATEDS_OK,ONCE_AND_ONLY_ONCE,任你选的QOS)。
恩,差不多了,剩下的API层面的改进,大家就在用的过程中去体会吧。
结束语:
研究规范,而又不囿于规范。
后面会陆续写一些文章,和大家分享一下自己在MOM方面的探索,希望你能喜欢~
最后,保持研究的严谨性,附上参考资料,大家有空可以自己阅读~
附录:
在工作中基于JMS 2规范开发了一套编程模型,如下:
参考资料:
1. http://www.oracle.com/technetwork/java/javaee/javaee7-whitepaper-1956203.pdf
2. http://jcp.org/aboutJava/communityprocess/final/jsr343/index.html