2.2 发布者-订阅者(Publisher-Subscriber)模式
一旦消息通道需要支持多个消费者时,就可能面临两种模型的选择:拉模型与推模型 。拉模型是由消息的消费者发起的,主动权把握在消费者手中,它会根据自己的情况对生产者发起调用 。如图所示:
文章插图
拉模型的另一种体现则由生产者在状态发生变更时,通知消费者其状态发生了改变 。但得到通知的消费者却会以回调方式,通过调用传递过来的消费者对象获取更多细节消息 。
在基于消息的分布式系统中,拉模型的消费者通常以Batch%20Job的形式,根据事先设定的时间间隔,定期侦听通道的情况 。一旦发现有消息传递进来,就会转而将消息传递给真正的处理器(也可以看做是消费者)处理消息,执行相关的业务 。
推模型的主动权常常掌握在生产者手中,消费者被动地等待生产者发出的通知,这就要求生产者必须了解消费者的相关信息 。如图所示:
文章插图
对于推模型而言,消费者无需了解生产者 。在生产者通知消费者时,传递的往往是消息(或事件),而非生产者自身 。同时,生产者还可以根据不同的情况,注册不同的消费者,又或者在封装的通知逻辑中,根据不同的状态变化,通知不同的消费者 。
两种模型各有优势 。拉模型的好处在于可以进一步解除消费者对通道的依赖,通过后台任务去定期访问消息通道 。坏处是需要引入一个单独的服务进程,以Schedule形式执行 。而对于推模型而言,消息通道事实上会作为消费者观察的主体,一旦发现消息进入,就会通知消费者执行对消息的处理 。无论推模型,拉模型,对于消息对象而言,都可能采用类似Observer模式的机制,实现消费者对生产者的订阅,因此这种机制通常又被称为Publisher-Subscriber模式,如图所示:
文章插图
通常情况下,发布者和订阅者都会被注册到用于传播变更的基础设施(即消息通道)上 。发布者会主动地了解消息通道,使其能够将消息发送到通道中;消息通道一旦接收到消息,会主动地调用注册在通道中的订阅者,进而完成对消息内容的消费 。
对于订阅者而言,有两种处理消息的方式 。一种方式是广播机制,这时消息通道中的消息在出列的同时,还需要复制消息对象,将消息传递给多个订阅者 。例如,有多个子系统都需要获取从CRM系统传来的客户信息,并根据传递过来的客户信息,进行相应的处理 。此时的消息通道又被称为Propagation通道 。另一种方式则属于抢占机制,它遵循同步方式,在同一时间只能有一个订阅者能够处理该消息 。实现Publisher-Subscriber模式的消息通道会选择当前空闲的唯一订阅者,并将消息出列,并传递给订阅者的消息处理方法 。
目前,有许多消息中间件都能够很好地支持Publisher-Subscriber模式,例如JMS接口规约中对于Topic对象提供的MessagePublisher与MessageSubscriber接口 。RabbitMQ也提供了自己对该模式的实现 。微软的MSMQ虽然引入了事件机制,可以在队列收到消息时触发事件,通知订阅者 。但它并非严格意义上的Publisher-Subscriber模式实现 。由微软MVP Udi Dahan作为主要贡献者的NServiceBus,则对MSMQ以及WCF做了进一层包装,并能够很好地实现这一模式 。
2.3 消息路由(Message Router)模式
无论是Message Channel模式,还是Publisher-Subscriber模式,队列在其中都扮演了举足轻重的角色 。然而,在企业应用系统中,当系统变得越来越复杂时,对性能的要求也会越来越高,此时对于系统而言,可能就需要支持同时部署多个队列,并可能要求分布式部署不同的队列 。这些队列可以根据定义接收不同的消息,例如订单处理的消息,日志信息,查询任务消息等 。这时,对于消息的生产者和消费者而言,并不适宜承担决定消息传递路径的职责 。事实上,根据S单一职责原则,这种职责分配也是不合理的,它既不利于业务逻辑的重用,也会造成生产者、消费者与消息队列之间的耦合,从而影响系统的扩展 。
既然这三种对象(组件)都不宜承担这样的职责,就有必要引入一个新的对象专门负责传递路径选择的功能,这就是所谓的Message Router模式,如图所示:
推荐阅读
- Java 读取本地 JSON 文件
- Java中如何实现线程的超时中断
- 1分钟带你看懂Java内部类
- HTML5 的JavaScript 客户端PDF解决方案——jsPDF
- JavaScript核心概念归纳整理
- 如何批量导入电话号码到手机通讯录
- 苹果手机怎么批量删除通讯录联系人?简单实用方法总结
- Javascript事件轮询
- 这篇java的NIO编程,保证你能看懂
- 1分钟读懂JavaScript、Ajax、jQuery全部知识点