1--消息传递系统(原书第三章)

时间:2024-03-04 20:13:55

消息传递系统是如何共享数据的一个解决方案,它的存在使被集成的各个应用可以把重点放在需要共享哪些数据上。

PS:术语采用微软的名称。

1、 消息通道(Queue)
消息通道是根据已在系统中公布的规则预先设置好的消息容器。
通道技术是异步通信的核心。Sender和Receiver尽管可以不知道彼此在何处,但一定得知道消息应放置在哪个队列中。
一个新的消息传递系统一般没有任何的通道,只有根据规则开始设置好队列后才能开始通信。
PS:原文中称为通道(Channel),这个名称和Pipe的中文名称“管道”很容易引起混淆。而且MS用的“队列”更形象化地表示出这是一个容器。

2、消息(Message)
数据的容器,携带着数据进入专门为了传递它到各个需求方而存在的系统。
不直接传递数据,却使用消息来封装。是因为各个应用对数据格式及依存的环境可能存在不同的定义,而且数据本身并不适合于复杂的传递环境。消息对封装的数据本身并不感兴趣,它的主要任务是描述所传递的信息、信源、信宿,进入队列等待被系统化的领取。
消息的组成部分:
消息头(Head)你可以把它看作信封或贴在包裹上的标贴:上面写着这里面装的是什么、从哪里发出来(信源)、得发送到哪去(信宿)。 
消息体(Body)信的内容或包裹里面的礼物。嗯,邮递员是看不见偶们在情书里面写了什么。当然,他的任务是送信,我们的私人信息和他是无关的。

3、管道和过滤器(Pipe & Filter)
过滤器是对消息携带的数据进行黑箱处理的步骤。原文中举的例子:对收到的消息中携带的文本进行加密和压缩后继续传递。过滤器是专门针对消息携带的数据,并不改变消息的流向,对消息头也不作处理。

管道:在原文中没有专门的描述,对它的作用和目的完全和通道混淆了。
根据我的观察,管道应该是特殊化的堆栈式队列,其入口和出口在建立时就已经被定死了指向。其主要功能是为过滤器提供专用的队列缓冲。过滤器的入口需要一个管道来提供缓冲功能,因为过滤器是作为消息系统的内部处理机制存在,必须考虑它的处理延迟。

流水线处理:这个要看书上的图形可以理解得更快。不过本意相当地简单,把单线程的串行处理拆解成多个单元,单元间用异步消息连接起来(也就是加入一个队列来缓冲上个单元处理完的消息,提供给下个单元处理)。这样就变成了多线程,因为单元B在处理单元A传给它的数据的同时,单元A报告其处于空闲状态以得到新的消息进行处理。在工序上有先后关系的A和B手上都有活干,这就是流水线了。
这种模式最大化了吞吐量。不过因为串行处理的最高速度是取决于手最慢的那个工人,这时就需要给他加个帮手(并行化处理)。

并行处理:如果串行化的多个过滤器中,有某个过滤器的处理延迟不能被忍受,可以提供多个相同的过滤器并行处理入口管道堆积的消息。采用这种模式的话,入口管道需要附加竞争消费者机制(点对点通道的功能)。如果入口管道内堆栈的消息是有序的话,那么并行处理器的出口管理需要附加重排器来恢复序列。

PS:过滤器(Filter)是个旧名词,在偶出生时就已经存在于通信领域(原文中记载了一个1978年提出的Pipe&Filter概念,-_-………),是个被传播得很广泛的专业名词。目前它承担的工作并不单纯地阻止某些消息或删除消息内的部分部内,它承担的工作变得多样化,所有的数据处理都可以在它内部进行。所以“组件”这个名称已经更适合它的角色。当然,我们仍然叫它过滤器,因为那些比我们更理解这个概念的家伙已经习惯于这个名词了。

PS2:原文中用C#和MSMQ作了一个过滤器的基类Processor (Oh my god,微软这边又把它叫作处理器了………看来偶真的是MS Fans啊,看完概念和草图偶也是叫它处理器来的。不过,I like C1’s samples and I hate MSDN.)。
HEHE,不发牢骚了。这段代码实例了Pipe&Filter概念。该类有两个外部接口inputQueue和outQueue,是MSMQ的MessageQueue类对象,分别代理入口和出口的异步通信和缓冲机制。
类的核心是消息处理方法ProcessMessage的触发和实现(基类中不编写实现部份的代码,由具体类重载),由inputQueue的ReceiveCompleted事件触发并将传入的Message对象传递给ProcessMessage处理。处理完毕传出outMessage对象,该对象被Send到outQueue对列中作为处理结果,完成了一次处理过程。
在代码的末尾,重新调取inputQueue的BeginReceive方法,回到起始的监听状态。

4、路由器
路由器的出现是必然的。路由器为消息系统提供一对多的支路机制。(一个没有可选择支路的系统是不存在的。如果有的话,那真是太好了,流程图就会象瀑布一样直通通地冲下来啊,多么爽快啊。
消息系统也需要判断消息的流向,这就象我们的代码中肯定会有IF和SWITCH一样自然。

路由器的特征:不对消息作任何的处理,只对消息的传递指定一个指向。
路由器根据内部编写的逻辑,判定消息应该进入连接在路由器上的哪一个出口通道。插入路由器可增加流程的可扩展性,路由器集中了所有的逻辑判断,每个支路上的过滤器只需要关心如何处理消息即可(处理器化)。当增加一种新的情况时,只需要修改路由器的逻辑,并插入新情况的处理组件即可。如果不想一有扩展就必需修改路由器,可以参见原文中给出的在后面章节会解说的方案。

固定路由器是只有一个输入和一个输出通道的路由器。可以用来作为将来扩展的预备,或者当作中继器使用,为其它不具备消息汲取功能的被动组件(转换器或通道适配器)提供中继。

路由器的状态:无状态的路由器“一次只根据一条消息作出路由政策”(原文引用,超贴切)。能保存状态的路由器可以提供其它的功能,比如记录一个经过的消息头列表,发现传入的消息与列表重复,就将重复的消息路由到预备被销毁的通道。

路由器和控制总线:路由器与控制总线的连接,可以把全局变量引入到路由器的路由策略。路由器中不可改变的逻辑被称为硬编码。相当于硬件路由器的BIOS吧。

当然,Pipe&Filter模组也可以实现通过条件判断来阻挡不需要的流向。这样作会让Filter因为合成了条件逻辑而降低复用性,还会让该模组与其它模组组合时会因为次序的原因出错。更糟的是,Filter是单向的,它即不能把消息转递到出口管道,也不能销毁它,因为另一个分支正等着读取它。为了把已被自己消费的无用消息放回原来的通道中,必须编写一个新的不符合标准的管道来把实现。所以用Pipe&Filter实现分支会变成紧耦合。

OK,未尾又到C#和MSMQ的示例时间了。
接口代码和上一节的Filter代码相似,把outQueue扩展成outQueue1和outQueue2,变成一对二的支路。
支路判定逻辑简单而有趣,HEHE。让路由开关象不倒翁一样均匀地左右摆动。如果是同一个办公室内按件平分工作量的两个办事员,这个逻辑就可以拿来用了。toggle=!toggle  ^_^ 。

5、消息转换器
用于实现两个消息不同层次间的格式互相转换。这个XLST语法挺麻烦的。暂时只会用到数据结构层次间的字段转换,偶就不细看了。以后再来查阅。