-- lostall
本文不介绍完整的基础知识,相关部分请参阅相关书籍
本文主要提供了一个例子,说明了COM+程序的一个简单的框架,程序运行的截图如下:
1、资源管理器RM(Resource Manager)
RM是一个可以跨越COM+事务并管理持久系统状态的软件。RM知道当参与事务的对象改变它所管理的资源时,
如何把资源的变化缓存起来。然后,如果事务被提交了,RM知道如何把这些变化发布出去;如果事务被放
弃了,RM知道如何把这些变化丢弃掉。
大多数的数据库都提供了COM+资源管理器,包括SQL Server、Oracle、DB2等。另外比较特殊的是微软消
息队列管理器MSMQ也提供了这种功能,这样把消息队列与事务结合在一起,也可以通过SetComplete来保
证放在消息队列中的方法调用能正确地发送到服务器上。
事务的最终提交过程是由RM来完成的,既SetComplete、SetAbort之后的打操作。RM通过两阶段提交方法
来完成这一过程。第一个阶段是准备阶段,RM主要工作是把所有相关信息存到日志中,日志是关键的数据
结构,不同的RM有不同的实现方法。如果RM从准备阶段返回成功,则对客户来说就意味着提交已经成功。
尽管数据库这时还没有真正的更新,剩下的工作都是RM的责任了。既RM承担了使变化永久化的全部责任。
比如说如果准备阶段成功完成之后数据库完成更新这前系统崩溃了,则在下次系统重启时,RM会询问DTC,
完成剩下的工作。第二个阶段是提交阶段,RM负责完成最后的数据库更新操作。
RM要实现的一个关键接口是ICrmCompensator,通过它的接口方法来完成事务的提交过程,调用过程如下图:
事务提交: 准备阶段: BeginPrepare()
PrepareRecord()
EndPrepare()
提交阶段: BeginCommit()
CommitRecord()
EndCommit()
事务中止: BeginAbort()
AbortRecord()
EndAbort()
2、安全性
要想正确使用COM+提供的安全性,必须在Component Service中正确的设置,包含以下步骤:
(1)在应用程序的属性/安全设置/授权中选中"强制对应用程序进行访问检查"
(2)在应用程序的属性/安全设置/安全级中选中"对进程级和组件级运行访问检查"
(3)在组件的属性/安全设置/授权中选中"强制组件级访问检查"
(4)在应用程序中加入适当的角色
(5)在组件的属性/安全设置中选中允许访问的角色
这五步缺一不可,且其中有(2)就必须有(5)。另要注意时常关闭前一次的应用服务程序
3、及时激活JIT(Just-In-Time activation)
JIT特性对COM+组件不必须的。事务型组件必须支持JIT,非事务型组件可以使用JIT也可以不使用。
对象激活(Activate)的时机:
(1)调用CoCreateInstance创建对象后,第一次调用组件的方法时激活对象
(2)对象处于无效状态时,调用对象的方法时会激活对象
对象无效(Deactivate)的时机:
(1)调用SetComplete或SetAbort后会使对象无效。这时会销毁对象(如果有对象池就放入池中,如果没有
就delete它),但代理/存根、RPC通道不变。
(2)可以设置在方法调用结束后使对象无效,其效果等同于调用SetComplete和SetAbort的效果。
(3)调用Release会使对象无效。这时会销毁对象(如果有对象池就放入池中,如果没有就delete它),同时
销毁代理/存根及RPC通道。
JIT发挥作用的条件:JIT的作用在于节省系统资源。但由于对象被无效时代理存根和通道并没有被释放,所
以只有当对象消耗了比这些COM+的基础设施更多的资源时,JIT才真正发挥了作用。
4、对象池
对象池与JIT激活两种机制结合起来是COM+获得最大速度的途径。JIT保证了对象可以及时被释放,对象池
避免了对象被频繁地创建。
池对象必须满足三个条件:
(1)必须实现IObjectControl
(2)必须是可聚合的(否则创建组件时会出错)。这是因为COM+在组件外面加了个包装器,可能是Context Wrapper
(3)强烈推荐线程模型为Free或Neutral型,但并非必需。这是因为池对象可能会被不同线程模型的客户使用,
使用Free或Neutral型会提高访问速度,避免公寓间调度。另外在COM+中强烈推荐组件为Neutral型公寓。
对象池发挥作用的条件:对象池的作用在于节省了组件创建的时间,这样只要做一些初始化工作就可以了。但
只有初始化对象的时间比创建一个新的对象的时间少很多的时侯,对象池才真正发挥了作用。
5、连接点问题
在本文给出的例子中使用了传统的连接点方式来实现组件对客户的事件通知。但这种方式在COM+有一些问题。
(1)本例中,客户程序首先通过CoCreateInstance创建对象,然后调用Advise建立连接,然后调用组件方法。
在组件的Activate、Deactivate和CanBePooled三个函数中我试图通过连接点来向客户程序返回信息。但
是通过运行程序,发现在对象创建时和对象销毁时连接点没有发挥作用,在其他时侯连接点工作正常。其原
因在于,当调用Advise时,COM+先调用了Activate,通知组件它已经被激活,然后才调用组件其他相关函数
以建立连接,所以这时在Activate中不可能通过连接返回信息。同样的道理,在组件销毁时,COM+先去除了
连接,然后才调用Deactivate和CanBePooled,所以这时也不能通过连接返回信息。
(2)对象是有状态的,它含有连接的信息。如果在Deactivate里删除了这种连接信息,那就意味着客户程序每
次在对象被无效后都要重新Advise以建立连接,有时这是很不方便的。但如果在Deactivate里什么也做的
话,就会出现本例中的有趣情况:
第一,每次去活后再重新激活,连接仍然存在,显然这是因为对象仍保留着原来的状态。
第二,如果同时运行多个客户程序,则在一个程序中操作的结果会同时显示在其他程序中。这是因为本例中
设置每个函数返回后都会无效,所以对象始终是放在池中的,即一个时刻只会有一个程序调用组件(因为是
在一台机器上,不会并发执行),所以操作的是同一个对象,连接点就连接了多个客户了。
COM+中有一种新的实现事件的方法,既发布者和订阅者。但这种方法有一些局限,能否取代传统的连接点还有
待进一步学习实践,我将另文阐述这个问题。