怎么学习COM+

来源:互联网 发布:多益网络校招问题提问 编辑:程序博客网 时间:2024/04/27 20:37

怎么学习COM+?[绝密]
怎么学习COM+
看事物看本质,整体框架和层次了解了,细节问题迎刃而解。
MTS[Microsoft Transaction Server]就是Microsof的事务服务器。从技术本质上说,就是基于DCOM技术,能够提供数据库分布式事务处理的软件。因为这类软件构筑在*作系统,数据库,消息系统,网络通讯之上,又在应用程序之下,所以这类软件,人们俗称中间件。从它的技术实现角度看,我们学习的重点一个是DCOM,一个是分布式事务。
DCOM基于COM,DCOM间本地通信用共享内存,机器间用与传输协议武官的WINSOCK接口。COM间方法调用采用RPC机制。因此,我们学习DCOM其实只学习COM原理,共享内存,WINSOCK和RPC机制。因为MTS可以多线程运行,我们还需了解COM的线程模型和多线程。另外MTS要求COM组件必须以DLL的形式存在,所以我们还需要了解DLL的工作方式。
关于数据库分布式事务。其实MTS自己本身并没有具体代码实现数据库事务提交。这一个任务是由MSDTC和类似的事务监视器[如IBM CICS等]完成的。MSDTC与数据库直接打交道,把分布式事务提交的请求传给数据库。数据库自己才真正实现事务管理的代码。而MTS并不和MSDTC直接相连。它是通过数据访问接口[如ADO,ODBC,X/OPEN等]和MSDTC通讯的。因此我们学习分布式事务只需学习数据访问接口和MSDTC如何实现提交请求就可以了。
说的彻底点,MTS号称实现的三大POOLING:线程连接POOLING,数据库连接POOLING,组件POOLING。线程POOLING是多线程的激活与挂起原理。数据库连接POOLING时由数据访问接口提供的。其本质也时多线程的应用,挂起时就把环境保存到一个数据结构里。至于组件POOLING则时COM引用记数和MTS相互协作完成的。这一块,我会详细讲解一个COM的产生,激活,运行,POOLING和销毁。
在此,我没有介绍MTS的安全机制,因为它是利用的时WINDOWS的安全机制。
介绍到此,你就会发现,MTS并没有什么技术内涵,它死心塌地地利用了*作系统,数据库,网络通讯协议的功能,组成了一个用户可用的东西。这也是WINDOWS DNA的灵魂精神,即借助底层分散的功能专一的组件,组成强大的如七巧板可任意组合的系统,甚至是爬在INTERNET上的一个巨大系统。这也是WINDOWS.NET战略和C#的出现原因。
好了,以下各期我会给大家把MTS实现的诸多技术基础一一讲解,如果你认为上述技术你已精通,那么去试着创造一个MTS吧,相信自己的技术能力。[代续]

中间层方法调用原理
客户端如果要调用一个COM接口方法,必须先获得这个接口的句柄。
客户端按COM组件的位置通过SCM[服务控制管理]找到COM所在的机器。
服务器端的COM通过查找注册表找到这个CLSID的DLL容器,通过调用DLL容器中的DLLGETCLASSOBJECT函数导入DLL。在导入DLL时,首先运行的是在DLL PROJECT文件中USES的单元的初始化代码部分,因为第一个是COMSERV单元,在此单元的初始化部分先建立了一个叫 COMSERVER的全局变量,它会记载DLL中所有类工厂的信息,然后执行每个USES单元的初始化代码,类工厂就是在每个单元的初始化部分产生的,这时候接口才与它们的实现代码真正绑定在了一起,这就是接口的妙用。他们在产生的时候向COMSERVER注册自己的存在,让COMSERVER统一管理起来。初始化完,就轮到DLLGETCLASSOBJECT真正执行了。通过线程模型和实例模型的设置,DLLHOST为这个接口创建一个CONTEXT环境,保存它的线程信息和引用记数信息。DLLGETCLASSOBJECT会根据你送给它的IID和类工厂的CLSID建立一个真正的COM实例,类工厂会根据绑定的实现类的大小负责分配内存,把实现类的代码放到此处,建立函数指针表,指向真正的函数,又建立了一个指针,它指向函数指针表,它就是接口引用。本质上接口就是一个接口实现类的实例的函数表指针。
从此你就可以通过接口这个指针调用接口方法了。至于接口指针在各自进程空间怎么传递是RPC和IPC的事,不用COM负责。VB等不具有指针或显式内存分配的语言是通过名称调用COM的,这个COM必须实现IDISPATCH接口,这个接口有一个方法,GETIDOFNAME,用名称找到ID,INVOKE通过ID再调用函数,所以VB调用COM会慢一些。这种通过名称调用的技术,有一个商业名称,叫AUTOMATION 技术

MTS如何保证事务链
MTS有三个标志来保证串行事务:Doomed Flag,Happy Flag,Done Flag

Doomed Flag:
一个Transaction中,不管有多少个MTS Object只会有一个Doomed Flag,而MTS会在何时Check Doomed Flag呢?
   1.程式执行的控制权由Root Object交还给Client时。如果Doomed Flag是True便会
     Abort Transaction并Deactivate所有物件而留下Root Object。如果是False则什
     么事也没有做。
   2.Root Object被Deactivate时。 Doomed Flag=True 时会Abort Trans,若为False
则会Commit Trans

Happy Flag:初始值是True,而每个有Transaction的MTS Object会有各自的Happy Flag。
   当该Object被Deactivate时,MTS便会检查Happy Flag,如果被设成False,那会把大家共用的Doomed Flag设为True,而且就不让其他人设Doomed Flag为 False。(这也就是为什么只有要Object执行SetAbort时,其他Object设成SetComplete也没有用,最后一定是Abort Trans的道理 )
而如何设定Happy Flag呢,可用两套方法:EnableCommit/DisableCommit 与
SetComplete/Setabort,EnableCommit单纯的设Happy Flag=True,而DisableCommit/
则设为False,但是Transaction仍未结束,一直到该Object Deactivate时,才会真正
查看Happy Flag,而决定要Commit/Rollback,因此,程式的执行过程中可以多次设
定EnableCommit/DisableCommit,只有最后一次有效。而SetComplete/SetAboet也会
设定Happy Flag为True/False,但是二者更多设定了Done Flag为True。

Done Flag: 这个Flag和Happy Flag一样,每个Object会有自己的一个Flag。
   MTS之所以可以在Client与Com Object间运作,在于它在Client与Com Object之
   间做了一个仲介(有一个context wrapper的东东在执行),所以MTS可以掌握Client/Object的资讯,当一个Object的Method透过context wrapper传回时(在Root Object是传回client,而在非Root Object则是传回呼叫它的Object,而我们在MTS中使用CreateInstance方法,事实上便是使产生的物件能透过context wrapper的控制),便会Check这DoneFlag,如果它是True,则会顺带把该Object Deactivate。所以了,如果我们用SetAbort的方法,之前说过它会设Happy Flag=False,且Done Flag=True,而在呼叫SetAbort的Function传回时,会Check Done Flag,发现它是True便会Deactivate该Object,而Deactivate Object 时会查Happy Flag,发现它是False,于是把Doomed Flag也设成true。

正因EnableCommit/DisableCommit没有设定Done Flag,所以使用它们时,是令Object有
State的,该Transaction会一直存在,如果我们已修改了某些Database的资料,那么该
lock是依旧存在的,不像SetCommit/SetAbort会令Transaction、State结束。而且在第二层Object 于EnableCommit/DisableCommit后把控制权交回Root Object时,第二层的Object并没有Deactivate,所以Root Object仍有机会呼叫其他的方法令第二层的Object再呼叫EnableCommit/DisableCommit以改变其Happy Flag。要避免于Root Object上使用
EnableCommit/DisableCommit,因为这样做代表Transaction未完成(不管是Commit/Rollback)便把控制权交还Client,那便是由Client决定何时要完成Transaction,那可能会造成Transaction的时间过长,易造成Dead Lock,所以在MTS上可设定Transaction多少时间后便是 TimeOut(内定60秒)

如果第二层的Object(由Root Object用CreateInstance产生的Object),没有呼叫
SetAbort/SetComplete,那该Object会一直保留著,直到Root Object对其reference
 Release掉。也就是说,Root Object可以呼叫第二层Object的方法多次后,才来做
SetComplete的动作,此时会由第二层的Object先Deactivate再来才是Root Object,
因为第二层没有呼叫到更改其Happy Flag的指令,所以它会是预设的True,而Root 
Object现在的Happy Flag也是 True下,自然Doomed Flag也是内定的False而造成
Commit Transaction。

如果第二层的Object SetAbort时,在该Object Deactivate时会令Doomed Flag=True,
而且控制权会回到Root Object,这时,Root Object不可以再使用SetComplete,否则
会产生Run Time Error给Client端,这时的Root Object只能呼叫SetAbort啦!!。如果
Root Object也不呼叫SetComplete也不呼叫SetAbort,那么,MTS会因看到Doomed Flag
=True而Release Transaction,并把所有第二层的物件Deactivate,而空留Root Object
,如此后来的呼叫可能会产生一串不可预期之果。 

所以结论是下层的Object SetAbort后,其上层也一定要SetAbort,并止停止其他的工作
,而我们要如何得知下层Object到底是SetComplete/SetAbort呢?可惜的是没有一个可以
查Doomed Flag的方法,所以变成我们要自己用传回值或Err物件的方式来传递。

COM+线程区别
1. COM Apartment的背景
  大家都知道,在一个多线程的*作系统中,在线程中对一个多个线程公用的变量进行*作
时,线程的同步是必须的.这个变量可以是一个简单的Integer类型,也可以是一个class或是
一个COM对象. 对一个简单的Integer变量来说,线程的同步很简单,每次对它进行*作的
时候用Mutex等进行同步. 对于一个class或COM对象来说,你也可以采用对简单变量一样的
方法,但更好的方法是在其内部进行线程同步,这样便于使用.也就是说,你在实现这个class
或COM对象时,就要写线程同步代码.如果class或COM对象内部实现了线程同步,那么它就是
Thread-safe的.
  现在的问题是,并不是每个人在写COM对象都保证它是Thread-safe的. 如果没有COM的
Apartment, 那么我们对所有开发的COM对象都要贴上一个标签指明它是不是Thread-safe,
这样使用这个COM对象的人才知道他如果要在多线程方式下使用这个COM对象时是不是要
进行线程同步.
  COM的Runtime为了使大家不用在自己开发的COM对象上贴上这么一个标签,而开发人员
可以在没有写线程同步代码的情况下照样可以用不是Thread-safe的COM对象,引入了
Apartment.

2. COM Apartment的概念
  为了解决上面所讲的Thread-safe问题,引入了COM Apartment概念.但到底COM Apartment
是什么? 大家考虑一下这个问题: 如果我写了一个不是Thread-safe的COM对象,把它交给
一个使用者,而且告述他是Thread-safe的.那么如果使用者在多线程环境下用我写
的这个COM对象就不会写线程同步代码,会出现什么情况? 答案是明显的,执行结果会
有问题. COM的Runtime为了使使用者在开发者没有告述他COM对象的Thread-safe问题的
情况下也能在各个线程模式下安全使用,要求一个COM对象能够告述COM的Runtime环境它
能在什么线程模式下被安全使用, 同时,使用者在使用一个COM对象之前,也必须告述
COM的Runtime环境他将在什么线程模式下使用这个COM对象,如果两者的线程模式不一样,
那么COM的Runtime环境就会介入,为它们完成线程的同步问题.COM Apartment就是COM的
Runtime环境对COM Client和COM对象的线程模式的包装(实际上是在TLS里面加上了线程
模式的标志). 在开发一个COM对象时开发者必须指定这个COM对象的线程模式(这一点大家
应该都已经知道了). COM对象的线程模式会在它注册时写入系统的注册表. 使用者在每个
使用COM对象的线程中必须首先调用CoInitialize,CoInitializeEx等来告述COM Runtime
环境将在那种线程模式下使用COM对象.

3. COM对象的建立与调用
  在一个COM对象被建立时,COM Runtime会根据它在注册表中指定的线程模式来建立它的
Apartment.关于COM对象的各种线程模式我不想在这儿多说,到处都能找到有关的说明.
关于Apartment的模式有以下几种:
Primary Single-Threaded Apartment: 这种Apartment在一个进程中只会有一个, 
   而且只处在第一次建立它的线程中.它对应Single线程模型.(PrimarySTA)
Single-Threaded Apartment: 这种Apartment在一个进程中会有多个.
   它对应Apartment线程模型.(STA)
Multi-Threaded Apartment: 这种Apartment在一个进程中只会有一个,
   它对应Free线程模型.(MTA)
Any : 这种Apartment在一个进程中可能有多个,也可能只有一个,
   它对应Both线程模型.(STA/MTA)
Thread-Neutral Apartment: 这时在COM+中出现的, 它一直执行在COM Client的
   进程之外的独立的进程之中.对应Neutral线程模型.(TNA)

COM Client在指定它所使用的线程模型时在调用CoInitializeEx是第二个参数用
Single-Threaded Apartment(COINIT_APARTMENTTHREADED)和
Multi-Threaded Apartment(COINIT_MULTITHREADED)来指明进入的是那种Apartment.

COM Runtime在建立COM对象时根据它在注册表中指定的线程模式将COM对象建立相应的
Apartment之中, 然后根据COM Client所使用的线程模式, 对COM调用采取不同的动作.
下面我想讨论一下不同COM Client和COM对象的线程模式COM Runtime采取的动作,但对于
Primary Single-Threaded Apartment,因为这种模式效率不好,基本已经不用,所以对它
不进行讨论.

1)COM对象为STA,Client线程初始化为COINIT_APARTMENTTHREADED
  COM Runtime将建立STA COM对象,Client的线程进入这个STA并
直接得到COM对象指针. 

2)COM对象为STA,Client线程初始化为COINIT_MULTITHREADED
  如果这个COM对象没有被建立过,COM Runtime将为这个COM对象建立一个
新的线程并在这个新线程中建立STA COM对象, Client线程进入的是MTA,得到的将是
新的线程中这个COM对象的被marshal的指针.如果这个COM对象被建立过,COM Client线
程得到的将是已经建立的STA线程中COM对象的被marshal的指针.

3)COM对象为MTA,Client线程初始化为COINIT_APARTMENTTHREADED
  如果这个COM对象没有被建立过,COM Runtime将为这个COM对象建立一个
新的线程并在这个新线程中建立MTA COM对象, COM Client线程进入STA,得到的将是
新的线程中这个COM对象的被marshal的指针.如果这个COM对象被建立过,Client线
程得到的将是已经建立的MTA线程中COM对象的被marshal的指针.

4)COM对象为MTA,COM Client为COINIT_MULTITHREADED
  如果这个COM对象没有被建立过,COM Runtime将建立MTA COM对象.
COM Client线程进入MTA并直接得到COM对象的指针.
如果这个COM对象被初始化过, Client线程进入已经建立的MTA并直接得到COM对象的指针.

5)COM对象为Any,COM Client为COINIT_APARTMENTTHREADED
  如果这个COM对象没有被初始化过,COM Runtime将建立STA COM对象,Client线程进入STA,
直接得到COM对象指针.

6)COM对象为Any,COM Client为COINIT_MULTITHREADED
  如果这个COM对象没有被初始化过,COM Runtime将建立MTA COM对象,
Client线程进入这个MTA并直接得到COM对象的指针.如果这个COM对象被初始化过,
Client线程进入已经建立的MTA并直接得到COM对象的指针.

7)COM对象为TNA
  这个从COM+开始的线程模式比较特殊,无论COM Client用那种方式初始化,它都
只存在于Client进程之外(DllHost.Exe中).而且Client用那种方式初始化都可以
'直接'进入TNA. 请注意,直接是打了引号的,其实,Client进入TNA时是通过marshal的,
只是这个marshal的作用稍微有点不同,这在以后说明.

4. 关于marshal
  在COM中marshal分为三种: 进程内的marshal, 同一计算机中进程间的marshal, 
以及不同计算机间的marshal. 进程内和进程间的marshal是通过Local RPC完成的,
计算机间的marshal通过DCE RPC来完成. 进程间和计算机间的marshal是必须的,
进程内marshal是在不同Apartment之间进行方法调用和传递对象Interface时发生.
在同一Apartment内的调用用不着marshal. 举个例子来说, 一个处于MTA中的Client线程,
想要调用一个处于STA中的对象时, COM Runtime会走进来, 对这个调用进行marshal.
为什么要marshal? 因为MTA本身说明了现在是一个多线程的环境, 而STA中的对象不是
Thread-safe的,那么对这个不是Thread-safe的对象的调用必须要序列化(排队).
COM Runtime为了保证不是Thread-safe的对象的调用序列化, 必须要截获对该对象的
调用, 然后进行排队. marshal就是起这个作用. 实际上,COM Runtime会截获MTA中的
线程对STA对象的调用(通过Proxy),将这个调用通过消息传递方式传递给STA对象的
stub, 在完成调用后由stub将结果传回Proxy. 对于对象的Interface, 也是同样的道理.
  对于COM+的Thread-Neutral Apartment比较特殊, 它是一直需要marshal的, 一个方面
是因为它处于不同的进程中.另外一个重要原因是, COM+提供了一系列新的功能, 如
Object Pooling, Object Construct String等. COM+必须要截获Client对COM对象的调用
才能完成将COM对象从缓冲池中取出以及放回缓冲池等的*作.
  那么marshal是自动还是手工完成的呢? 方法调用是自动完成的.对象的Interface的
传递,一般情况下,  是自动完成的,比如你通过调用CoCreateInstanceEx,
CoGetClassObject等得到的对象Interface,以及通过方法调用传递的对象Interface.
但是有些情况下必须手工marshal. 还是形象写,举个例子: 比如我有一个COM Server, 它
监视一个工控装置的信号, 如果信号有异常,它要通知客户端,让客户端进行报警动作.为了
实现这个功能, 客户端和我的COM Server通过IConnectionPoint完成事件触发机制.为了
提高性能, COM Server的主线程接受客户端通过IConnectionPoint的Advise传来的
Interface指针, 并将之放到一个Interface指针表里, 主线程运行在STA中. 另外建立了
一个运行于MTA中的线程专门用来监视信号,如果信号异常,它将调用Interface指针表里
所有Interface的方法来通知客户端. 现在如果理解COM的机制的人看到我这个实现方法就
知道这里面需要对Interface指针进行手工marshal. 为什么, 客户端通过IConnectionPoint
传给我的COM Server主线程的Interface指针是自动进行了marshal, 但是, 由于我的
COM Server的专门用来监视信号的线程运行在和主线程不同的Apartment之中, 对这个线程
来说, 这些Interface指针是没有经过marshal的, 在调用是就会出现RPC_E_WRONG_THREAD
错误. 要解决这个问题,有两个办法, 1) 让我的主线程也运行在MTA中.这种方法简单.
2) 手工marshal. 在主线程中得到客户端的Interface指针后, 调用
CoMarshalInterThreadInterfaceInStream, 得到一个IStream的指针,让后将它放到
IStream的指针表里, 监视信号的线程要通知客户端时,从IStream的指针表取得
IStream指针, 然后调用CoGetInterfaceAndReleaseStream得到marshal后的客户端
Interface指针. 这种方法有个缺点, 就是一旦调用CoGetInterfaceAndReleaseStream后
这个IStream指针就被释放掉了,下一次就取不到了. 更好的解决方法是采用GIT(global
interface table), 主线程将它得到的Interface指针放到GIT, 监视信号的线程从GIT
中取到的Interface指针是正确marshal了的. GIT是一个COM对象, 有三个方法提供
Interface指针的存取,使用也很简单,这儿就不多说了,具体请参照帮助.


构建稳定的服务器端组件
OLE中的进程线程介绍
一个进程是一个实际内存空间、代码、数据和系统资源的集合。一个线程是在进程
内连续执行的一段代码。进程管理器执行的是线程而不是,所以每个32-位的应用
程序至少有一个进程和一个线程。进程之间通过消息传递进行通讯,使用RPC传递
信息。
在COM中定义了三个 多线程模型,一般情况下这些信息适用于线程和进程。一个
进程至少有一个线程,称作"主线程",此外还可以有多个线程。一旦一个线程执行
后,直到它退出运行、高权限线程对它的中断、用户行为、系统核心线程的调度,
否则一直执行下去。一个线程可以运行多段代码,多个线程也可以运行同一段代码
。执行同一段代码时这些线程使用自己不同的堆栈保存数据,并且这些线程可以共
享进程的全局变量。
      在NT中进程调度根据进程和线程的权限来进行。可以使用函数 
SetPriorityClass()设置进程的权限,使用SetThreadPriority()设置线程的权限。
多线程应用程序必须避免两个线程问题:死锁、竞争。OLE调用不同apartment内的
不同对象来防止这种死锁的问题。同时OLE提供了一些函数来帮助在进程外服务器
内避免出现竞争问题。可以参考(Out-of-process Server Implementation
 Helpers)。
通常,了解OLE线程结构的简单方法是将COM在进程内的对象想象成分成了不同的组
,称这些组为:apartment(室) 。一个COM对象存在于一个对应的室内,这样只有
属于这个室的线程才可以合法地直接访问这个对象的方法。其他的线程如果要访问
这个对象必须通过一个proxy (代理)程序。在COM中有两种类型的室:单线程室、
多线程室。
l 单线程室:利用OLE的每个线程存在分别存在于不同的室内。
l 多线程室:在自由线程内的使用OLE和调用OLE对象的多线程室的同步由他自己完
成。
 单线程室只包含一个线程,所以这个线程内的COM对象只能得到属于这个室的这个线
程内的方法调用,在这个室中所有的COM对象的调用由*作系统的消息队列来同步。
多线程室包含一个以上的线程,所以在这个室中的COM对象可以得到任何一个线程的
方法调用。这个室中的线程使用一种称作"自由线程"的模式。OLE没有对MTA中的COM
对象的调用的同步支持。所以,这些COM对象在需要的时候要自己提供同步。
构建稳定的服务器端组件的七个步骤

实现健壮性能的规则

Hank Marquis
来自于Enterprise Solutions for Microsoft BackOffice and Windows NT Magazine

在你的服务器上安装了微软IIS(Internet Information Server),你就可以发挥ASP(Active Server Pages)的优势了,ASP利用ActiveX组件来为你的网络应用完成所有种类的工作。尽管你可以在HTML和有ASP页面的IIS里面使用许多ActiveX组件,服务器端组件也不是运行在一台服务器上的普通组件。它在运行时不会告诉你同需要特别关照的产品服务器有关的任何信息。你将无法做任何事情去改变其对服务器性能、安全和稳定性的影响。对服务器端的组件的不恰当选择可能会导致一些问题,包括速度的明显下降,安全漏洞或者其它更恶劣的问题。

客户端的组件在用户计算机上执行。客户端组件包括绝大多数我们现在已经了解的一些流行组件:标签控件,文本框,命令按钮,格子等。这些组件可以通过<OBJECT>标签和(或)HTML对象语法来包含在客户端HTML代码中。

多数的有用的客户端组件会提供特定种类的用户界面。记住,使用客户端组件就意味着真实组件已经被传到客户计算机上。寻常的做法就是把组件下载到客户计算机上。当然,用户不得不等待下载过程,而且客户计算机必须被配置为允许下载。 

与此形成对比的是,服务器端的组件在服务器计算机上执行。服务器端组件也为用户做一些工作,但却是在服务器上运行的。你必须认识到这个差异并且相应地编制代码。服务器端组件为你的整个应用程序封装了一些逻辑或功能。

当一个用户使用应用程序时,他将不会真正看到服务器端的组件。这些组件大多数都可以通过需要使用组件的ASP脚本中的<OBJECT>标识来被包含。你同样可以通过服务器的CreateObject语法来包含服务器端的组件。 

建造健壮的组件

用于创建健壮组件的好材料并不多。但是,我在这里向大家推荐七个关键步骤,它可以帮助你创建稳定和安全的服务器端组件,可以很优雅地缩放并且维持性能。在创建一个服务器端网络应用时,你需要把稳定,安全和性能放在你心目中的首要位置。 

服务器端组件不应该具有GUI(图形用户界面)。因为服务器端组件是在服务器上运行,网络应用的用户是看不到可能弹出的任何对话的。你的组件需要能够同脚本和其它组件进行交流,却无需同用户交流。避免所有的消息框和其它任何图形的用户界面单元。你必须开发利用返回结果来同状态和其它模块信息进行交流的代码。如果什么东西出问题,不要抛出一个错误消息或者使用一个消息框,可以返回一个状态变量。你需要做的最后一件事是锁定忙碌服务器等待OK按钮被按下。


服务器端组件不得被传递引用或者传递引用给对象。普遍的做法是把控制作为一个参数传递给其它过程或组件。这包括其它对象的引用,比如RecordSets。尽管如此,向网络中的组件传递引用可能导致速度明显下降,使一个繁忙的服务器更加缓慢,网络应用程序在响应用户需求时也表现得更慢。


服务器端组件应该尽可能地少含方法和属性。每一个方法或属性的调用都需要大量处理。因此,一个编写的好的服务器端组件应该几乎不含明显的方法和属性。组件含有的那些方法和属性会带来更多的参数。具有很多参数的调用越少,性能就越好,尤其是你的网络应用程序需要支持许多用户时。这个技巧和许多开发人员的经验是相反的。尽量少的使用带有许多参数的调用也会带来另外一些问题,这使得编码和调试更加困难,但是速度上的改进是与付出的努力相当的。


服务器端组件必须实现恰当的线程模型。利用单线程组件可能导致服务器限制一个线程的会话,这将带来速度的明显下降。应该选择VB的房间模型线程选项并且努力避免单线程组件。但是,VB不能创建你可以在Visual C++里发现的具有线程选择范围的组件,。这一点也表明VB不是一种很合适这项工作的开发语言。


服务器端组件应该使用早期绑定。如果你的服务器应用程序要扩容,这显得特别重要。早期绑定的对象在编译时就拥有引用解析,可以节省不少执行时间。 


服务器端组件不能使用在应用程序或会话作用域的声明中。请注意你的控件是如何被限定作用域的。作用域描述了如何创建一个组件的实例,这对你的服务端组件的成功起着关键作用。正如上月所讨论的那样,存在三种级别的作用域:页面级,会话级和应用程序级。页面级作用域对象可以用页面本身的HTML和ASP脚本代码来创建。页面级作用域组件的最佳性能可以通过使用房间线程来得到。而对应用程序级和会话级作用域组件来说,可以通过使用ATL组件的双线程模型来得到。同作用域结合的线程模型也影响服务器的安全性能。例如,一个利用应用程序级作用域的房间线程组件在系统安全环境里运行,但并不是当前用户的安全环境。这对那些具有安全意识的人来说可能是一个问题。


为了速度,服务器端组件应该是进程中组件;为了稳定,则应该是进程外组件。有两种方式去创建COM(OLE)服务器--进程中和进程外。在VB里,你用EXE或DLL扩展名去编译服务器。具有DLL扩展名的OLE服务器就被称为进程中服务器;而具有EXE扩展名的被叫做进程外服务器。进程外意味着组件作为一个独立的过程在运行,而且与调用它的应用程序不共享地址空间。运行进程外组件会导致性能的降低,因为Windows不得不在两个或多个应用程序之间来回移动数据。进程中意味着组件在调用它的应用程序的地址空间里运行。在过程之间交流无需中间物,这使性能显著提高。进程中组件的负面是如果组件失败,那调用它的应用程序也会失败。 
服务器端组件使得创建一流的解决方案成为可能。利用IIS,可能还得用上MTS,你可以基于Windows NT的强有力的处理能力创建高性能的可升级的网络应用程序。