全国服务热线4000-662-088 专线13828842088 在线咨询 预约方案申请

Java帝国之JMS的诞生

【摘要】2017-6-10    小B

 

                                                                           Java帝国之JMS的诞生

                                                                                                                                   2017-6-10   小B


         自从张家村的ZhangMQ问世以来,大家都看到了消息队列在分布式系统中的巨大好处,纷纷另起炉灶搞一套自己的消息队列,各种MQ产品如雨后春笋班出现,各家都疯狂的宣传自己的宝贝。


为了吸引程序猿来使用, 各家八仙过海,各显神通,定义了各式各样的API,    由于是独立发展,这些API协议多样,互不兼容, 学习成本高,使用起来非常不方便。


这是帝国所不能容忍的 !


其实Java 帝国非常擅长搞出标准的协议和接口, 之前的JDBC就是一个典型的例子(参见文章《JDBC的诞生》), 制定了协议以后, 让各个产品厂商去实现, 实现了针对数据库编程的统一接口。


既然数据库可以这么干, 消息队列肯定也没问题!


由于张家村开发了第一个消息队列产品, 帝国把制定标准接口的光荣使命交给了张家村。


2、消息队列接口设计


张家村经验丰富的老村长又把任务分给了小张, 告诉他我们要做的是一个厂商独立的标准接口, 让他先去调研一下时下流行的MQ的现状。


小张先找到了某大厂著名MQ,  它占据了企业级市场不少份额, 但是直接使用它的 Java API 编程的话就不那么容易了, 大家可以快速浏览下:


小张能看的出这是在发送一个消息,但这MQEnvironment, openOptions,MQPutMessageOptions 看起来让小张心烦,特别是还得理解Queue Manager这样的概念,有点不容易。


小张又找了一个以开源吸引人的RabbitMQ , 这个看起来清爽多了:


但是这queueDeclare方法 和 basicPublish 方法小张总觉得的不爽。


只看了两个消息队列, 小张就不想再看了, 他去找村长说: 这差别也太大了,根本无法统一。


村长说:”不要被纷繁的现象迷住了双眼, 要看透背后的本质, 做出适当的抽象才可以。“


又是抽象!  小张暗自叹气, 这抽象实在是太难了。


(参见文章:《 抽象,程序员必备的能力 》  )


”你深入思考下“  村长看出了小张的困难, 鼓励他说: ”其实也没那么难, 我们先搞出几个最基本的概念,  记不记得操作系统中学过的生产者-消费者模型?  我们完全可以应用到这里来啊,   消息生产者(Message Producer), 消息消费者 (Messge Consumer) ,  生产者提供发送消息的方法, 消费者提供接收消息的方法, 如果加上消息队列 (Message Queue) 的话就是这样:“




小张说:”这也太抽象了吧, 我看人家还有什么Queue Manager,  Connection ,Channel 之类的“


村长说: ”别急啊, 你看不管是生产者向队列发送消息,还是消费者去接收消息, 其实都是在和消息队列进行交互, 所以我们再引入一个会话(Session)的概念出来 。“


”奥,  我有点明白了 ,Session 可以创建消息, 还可以引入事务的支持呢“  小张思维敏捷


“不错,  其实消息生产者/消费者也应该由Session来创建,因为他们要发送/接收消息肯定是在一个会话中, 另外你想想, Session对象由谁来创建?”


小张说: “应该是Connection ” 说着小张画了一张图:






“你看这概念不就出来了,是不是很简单? ” 村长笑着说。


小张挠挠头说: “会者不难,难者不会啊, 对了,我们还缺乏最关键的连接参数(ip地址,端口等)还有队列的名称之类的信息。 这些信息怎么办?”


“这确实有点复杂,各个厂商的具体情况差别太大。” 村长也表示犯难 ,“你让我想想, 下午再聊。”


3、配置和代码的分离


小张中午吃饭的时候也在想, 这些复杂的配置参数该怎么办, 要是都让程序员在代码里写,那就太丑陋了吧, 因为不同的MQ产品,配置都不一样啊。


下午的时候,看到村长一副喜气洋洋的表情, 小张知道问题解决了。


村长说: “我想到了一个办法, 一个很简单,但是有效的办法。”


小张说:“别卖关子了,快说吧”


”其实也是又老又俗的办法了,  这个办法就是把配置和代码分开,  你不是说这些连接参数很复杂,各个厂商不同吗?  那就作为配置信息把它放到Web容器里,对外只提供一个简单的ConnectionFactory的接口,由这个ConnectionFactory来创建Connection, 当然了各个厂商必须实现这个ConnectionFactory“


"那怎么才能得到这个ConnectionFactory ?"


"这就简单了, 对程序员来讲,通过JNDI 就可以轻松拿到了, 例如:"


”这办法不错,把细节都隐藏起来了, 既然ConnectionFactory可以这么搞, 队列(Queue)的配置信息也可以这么办啊。“


村长说:”所以ConnectionFactory, Queue 就是隔离细节的抽象层。”


4、再次抽象


标准接口初具模型,小张很高兴,晚上请喜欢的张二妮吃饭, 忍不住得瑟了一下。


张二妮说:“你们两个老土,定义的标准接口,都已经过时了!”


小张很生气: “怎么可能呢?”


二妮说:“告诉你们吧, 你们搞的这个叫Point to Point模型,就是一个发送方,对应一个接收方, 现在外边有很多人在用 发布/订阅 的模型,你们知道不? ”


“一个客户端(Client1)对一个Topic发布了消息, 很多订阅了这个Topic的客户端(Client2, Client3) 都可以接收到这个消息的副本。”


小张呆住了, 这和以前ZhangMQ的方式完全不同, 队列都不见了, 引入了一个新的主题(Topic)的概念。


第二天, 小张赶紧去找村长, 告诉他发生了新情况。


村长说: “你呀,还是太年轻, 慌什么,深入思考一下, 这个发布/订阅的本质和我们之前的生产者/消费者没什么不同。 ”


小张说: “那人家还有Topic的概念呢。”


“我们可以把Topic和Queue 变成一个更抽象的概念,他们都是消息的目的地, 嗯, 就叫做Destination吧,这个Destination的细节也是需要配置出来的, 通过JNDI来获取。”


“那订阅怎么处理?”


村长说: “原来我们定义的是MessageConsumer, 现在增加一个新概念叫做 TopicSubscriber , 可以从Destination获取消息,这不就行了, 其实从本质上来讲Subscriber也是消息消费者的一种而已。”


“那怎么才能实现订阅的功能呢?”


“别忘了, 我们只定义接口行为, 具体的实现需要由各个产品来负责!”


小张看着这幅图, 深感抽象的威力巨大, 这么多的细节最后变成了这几个简单的概念!


小张还特意写了一段代码,展示上面的概念:

张家村把这个设计交了上去, 帝国很满意,把它起名为Java Message Service (JMS), 随后强制各大产品实现JMS, 否则就不颁发进京证, 没这个证别想在帝国做生意!



JMS由于设计良好,概念清晰,其实不用怎么强制,很快就流行开了,成为了Java 帝国的事实标准。


后记: 其实JMS规范的制定是在Sun的领导下,各大厂商密切参与完成的:


There were a number of MOM vendors that participated in the creation of JMS. It was an industry effort rather than a Sun effort. Sun was the spec lead and did shepherd the work but it would not have been successful without the direct involvement of the messaging vendors. Although our original objective was to provide a Java API for connectivity to MOM systems, this changed over the course of the work to a broader objective of supporting messaging as a first class Java distributed computing paradigm on equal footing with RPC.