一. 消息引用概述
Biztalk完全基于消息和消息订阅的机制,由发布服务器发布消息,消息代理根据订阅服务器的订阅条件判断发布的消息是否跟某个或某几个订阅匹配到,如果有匹配到消息代理机会把消息路由到所有的订阅了此消息的服务实例。如果没有匹配到就会抛出一个异常,告知这个消息没有订阅者订阅,无法路由。
可以看出消息跟订阅消息的服务实例是成对出现的,姑且称之为服务实例消息对。一条消息如果只有一个订阅者,那这个消息跟这个订阅的服务实例就是一对。如果一条消息有多个订阅者订阅,那么这个消息要同时发送给所有的订阅者,就会出现多对服务实例消息对。
同一个消息在biztalk的messagebox数据库中只保存一个副本,多个服务实例订阅同一消息时,各个实例跟这个消息之间是引用关系,不会各自创立一个消息副本。
消息保存在messagebox数据库的spool表和parts表中。SPOOL 表的内容是消息的总体性描述和消息的上下文属性,一条消息在此表中为一条记录。PARTS 表存放多部分消息的各个部分,一个部分在此表中占一条记录。哪一个是正文部分由spool表中的uidBodyPartID标识。
消息进入到biztalk系统后,路由到某个服务实例,就跟这个服务实例有了引用关系,在这个服务实例运行期间消息不能从数据库中删除。一旦这个服务实例运行完毕,这个消息也就不再使用,可以删除。如果有多个实例同时引用这个消息,则必须引用此消息的实例都完成后才能删除消息。
为了反应消息跟实例之间的引用关系,判断一个消息是否还在被某个实例引用,biztalk有着一套消息引用计数功能。
Biztalk采用消息引用计数表来记录消息的引用数,分为两种情况:如果消息只被一个订阅服务订阅,采用本地引用计数机制,如果消息被多个订阅服务订阅,采用全局引用计数机制,下面分别分析这两种情况下的消息引用计数功能的实现。
二. 本地引用计数
如果消息只有一个服务订阅,消息代理调用bts_FindSubscriptions存储过程时发现一个订阅,这个存储过程返回一条记录。
这种情况下,消息代理会在本地引用计数表BizTalkServerApplication _MessageRefCountLog中记入实例对消息的引用计数。
BizTalkServerApplication_MessageRefCountLog表有三个字段:
uidInstanceID ―― 服务实例的guid
uidMessageID ―― 消息的guid
nRefCount ―― 引用计数(值为1,表示引用了1次)
一旦服务实例运行完成,执行如下两个动作:
l 把BizTalkServerApplication_MessageRefCountLog相关记录删除
l 将此消息的guid写入MessageZeroSum表中,MessageZeroSum表中保存不再被引用的消息的guid,表示这些消息没有服务实例引用了,可以删除。MessageBox_Message_Cleanup_BizTalkMsgBoxDb作业最终就是根据这个表来清理无用的消息。
三. 全局引用计数
如果消息有多个服务订阅,消息代理调用bts_FindSubscriptions存储过程时发现多个订阅,这个存储过程返回N条记录。
这种情况下,消息代理会把消息引用计数记入到全局消息引用计数表MessageRefCountLog1或者MessageRefCountLog2中,这两个表结构一样,只是一个表是活动的表,另一个作为备用表。至于当时哪个表处于活动状态有ActiveRefCountLog活动引用计数表的相关字段指示。
MessageRefCountLog表的结构:
uidMessageID ―― 消息guid
uidInstanceID ―― 服务实例guid
snRefCount ―― 引用计数。此字段可以为正负,正表示被引用,负表示被放弃引用。
ActiveRefCountLog表的结构:
fType ―― =1 表示消息引用表
tnActiveTable ―― 哪个引用计数表为活动,可能的值为1和2。=1表示MessageRefCountLog1当前为活动表;=2 表示MessageRefCountLog2当前为活动表。
当N个服务订阅了同一个消息,消息代理记入一条引用记录:uidMessageID为这条消息的guid;uidInstanceID为空,因为有多个服务订阅;snRefCount设为N,表示这个消息被引用N次。
每个服务各自运行,一个服务完成后就会在此表中增加一条记录:uidMessageID为这条消息的guid;uidInstanceID为空;snRefCount设为-1,表示减少一次引用。如果所有服务都完成后,将会增加N条引用计数为-1的记录,正好跟正的N计数对消。
四. 总结
Biztalk中消息如果不特别的进行清理的话,将会一直堆积下去,不管这个消息是否已经不再有用。所以一般需要定期对messagebox数据库中的消息进行清理,把一些过期的无用的消息清理掉,以防大量无用的消息占用很多硬盘空间,也给系统的性能带来负面影响。
Biztalk对消息的引用有完整的记录,可以从这些记录中获知哪些消息已经不再使用,系统可以根据这些信息来清理消息。
关于清理消息biztalk本身提供了几个相关的作业。MessageBox_Message_ManageRefCountLog_BizTalkMsgBoxDb和MessageBox_Message_Cleanup_BizTalkMsgBoxD,它们就是根据这些引用计数的统计来完成清理消息工作的。关于这两个作业另外写文章介绍。