1.背景 本文续上篇《Java 帝国之消息队列》 自从张家村的帝国的诞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 帝国的事实标准。 【本文为专栏作者“刘欣”的原创稿件,转载请通过作者微信公众号coderising获取授权】 戳这里,看该作者更多好文