您了解应用程序的构造基础。您知道要使用的语言,以及服务的一般形态及其交互。不过,还有一些明显的问题。应该构造多少这种体系结构?共享哪一部分实现,它们的分布方式,以及处理特殊的通信情况如何?其答案涉及工程和技术一类的知识,不过没必要从零开始。
下面是有关应用程序设计和软件发布问题的有用的阅读材料。
让我们来探讨
有些时候您可能希望平台/运行时/数据访问机制能按这种或那种方式工作,但是它没有。另一方面,可能它确实正常工作,但是您无法了解如何使它的行为符合预期的方式。在这种情况下,能这样说就好了:“行啊。我知道如何编码,我会自己做(数据库连接池、对象分配器、异步消息分配器),这就行了!”
了解体系结构方式的一些原理是不错,但是要谨慎。很多情况下自己为状态管理、高速缓存或池设计体系结构,会导致性能或可扩展性的降低而不是提高。
此时您完成了与体系结构灵活性、实现和可扩展性有关的复杂设计决策。但是可能需要花费宝贵的时间来实现和测试它。
尽管 Visual Basic
已经发展了企业级的语言选择,但是它最适合于构造业务对象而不是管件或体系结构对象,后者需要更复杂的方式来处理复杂的技术问题,例如线程管理、接口汇集和并发访问。
详细信息
该问题不仅仅在管理对象的方式中存在,而且在最常遇到的陷阱(试图用不适当的工具来优化体系结构)中也存在。下面是一些例子:
- “我可以将对象保存在内存、全局变量或集合中,因此可以在需要时使用它们”。这种常见的误解并没有考虑在每个线程中都有一个全局变量的副本,而且对象可以执行它们需要的任何线程。VB6
对象是房间线程,因此在实例上执行的每个方法调用都必须在创建它的线程上。生成这些集合将破坏
MTS 线程池,并且使项目非常不稳定。
- “如果在调用之间将属性存储在我的对象中,那么网络流量就会减少”。这通常将导致有状态的对象,它会消耗更多的资源,例如事务锁、内存和网络流量。除非计划调用要求所有对象数据保持不变的几十个方法,否则无状态设计将更有效。建立网络连接是非常昂贵的,并且在这之后发送的数据量的多少几乎没有什么区别。
应用程序设计应该是线性的并且是简单的。将诸如连接字符串(字符串而不是对象!)的临时可共享状态存储在
SPM、将诸如客户信息的永久状态存储在数据库中,以及将诸如临时计算结果的活动范围状态存储在适当的函数范围变量中。
在服务器上采用无状态编程模型
该设计主题已经通过许多渠道进行了彻底的辩论,并且已经以不同方式得到实现。尽管采用无状态设计有许多原因,但是有状态设计也有自己的生存空间。然而,正确的软件发布方法建议,在大多数情况下,无状态方案对
OLTP 服务器操作(一种共享资源/并发独立的活动环境)是最优的。它们允许独立、方便地复制和设计服务,以及透明故障转移、更简单的负载平衡、更少的资源冲突以及更方便的事务数据管理。
此外,与有状态方式相比,无状态方式在设计和实现上更线性化。这意味着它减少了对随时间变化因素或者方法调用顺序的依赖,从而产生更独立的活动,并为负载平衡提供更小的粒度。与处理状态管理中的所有问题相比,从单个用户活动的角度出发开发软件要更为容易。
详细信息
在服务器对象中维护特定于用户/会话/实例的状态的特殊原因,在于以下因素都很严重的情况下:
- 保存的数据很昂贵或无法重新获得。
- 它经历了太昂贵的转换,或者无法在客户机器上完成。
- 它经历了太昂贵的转换,或者无法在客户机器上重新完成。
- 它太昂贵或者无法将状态数据传输到客户机上。
- 数据没有复制到其他事务存储区中。
如果上面大多数条件为真,那么就很有理由实现有状态的服务器端编程模型。这可能会提高性能,但是它会在很大程度上减少并发度甚至可扩展性。
此时,您必须问自己该数据块是否属于
OLTP-ish TP
环境。您可以评估其他状态管理方法(存储到特殊的数据库中)或者传输机制。同时评估异步操作在处理该类型信息时的优点。
操作方式
确保在服务器上有良好的状态管理方式:
- 您没有用于高速缓存记录集、集合或词典中信息的全局变量。它们将导致瓶颈和故障。
- 您的类没有类一级的成员(Private 或 Public 型变量),它们拥有超越一次方法调用的生命周期。使用函数的参数而非属性(网络往返更昂贵,而由于大量的参数网络数据包几乎不会被浪费)。一般来说,唯一的
Private 变量是从构造程序字符串初始化的变量。
- 您的方法打开了到资源的连接并立即关闭它们。所有已经打开的 ADO 对象将关闭,并且所有创建的对象都将通过设置为空而被显式释放。
- 在从方法返回之前调用 ObjectContext.SetComplete/SetAbort
避免将对象存储在共享属性管理器中
“共享属性管理器 (SPM)”设计为存储小块信息,以便用于单个写、多个读的方案。存储在
SPM
中的对象将受到多线程访问,并且将导致序列化瓶颈问题,它将强制对象专门与创建它的
MTS 线程关联。这意味着它无法利用 MTS
用来获得高可扩展性的线程池。
详细信息
SPM
可以用于存储诸如连接字符串和其他程序包或者应用程序的项目。也就是手头上有用的特定信息。
请记住在这种类别下找不到与事务相关的信息块,例如实现为数组的种类列表或者小型查询表。目前的
RDBMS(例如 SQL Server 7.0)都根据更小粒度的数据和先进的技术来优化内存使用,允许
RDBMS
启发式方法根据动态标准来确定要缓冲的内存,通常是更好的决策。
操作方式
如果将对象存储在 SPM
中并且确定信息属于那儿,那么可以将方法添加到允许从字符串对自身进行加载的对象,然后它将存储在
SPM 中或者把它的状态而不是实例存储在
SPM 中。请考虑到 SPM
允许类似于基本(属性和名称)或者类似于数组属性(属性和位置)的组,并且该名字空间可能能够在不增加额外字符串分析负担的情况下直接访问。
如果不必将信息多次写入 SPM,或者只需要很少的几个非关键要素(例如页面访问次数),那么您可能会发现 SPM
非常有价值。在存储到 SPM 之前,请确保要访问的项目不需要将故障转移到其他机器上,而且在进程关闭的情况下,任何丢失的数据是易于处理的。
避免传递 COM+ 对象引用
通常 COM
对象在完成任务时需要调用其他对象。它需要的所有状态都可以在调用本身之后被收集。在同步服务器环境中,必须从被调用者接收回调、或者必须将某些与中间任务有关的信息通知调用者,这些通常都表明设计过分复杂。
如果保持无状态设计,则不会遇到该问题。但是,如果对象确实应该将某些与函数返回无关的信息通知它的调用者(在客户机或者服务器本身),那么您可能会试图实现回调。除了会对父函数操作的原子性提出疑问之外,它还会导致复杂的同步问题。
注意
同时还需要确保在合适的时机将所有对象引用设置为
Nothing。
有许多重要的问题需要考虑,例如事务流、安全性等等。如果您调用的对象必须进行相反的调用才能完成它的任务,由于在这期间状态不会改变,那么为什么不在这之前收集子对象需要的所有数据、并进行函数调用呢?这样就可以得到更清晰、直观的设计。
操作方式
如果需要传递对象引用,则不需要使用 SafeRef 函数获得对自己实例的“安全引用”。COM+
可以确保对象引用是安全的(它们指向正确的上下文包装),并且 SafeRef 仅仅是为了向后兼容而保留的。
不要从 COM+ 对象产生事件
如果需要向客户发出通知,请不要使用事件(在类中声明“事件”,并用
RaiseEvent
产生事件)。这将使该方法在某些情况下无法获得线程安全性,并且会增加安全配置的复杂程度。
详细信息
使用来自于 COM+
组件的事件不仅带来了这些技术问题,而且还产生了一个关于事务对非事务和易错的资源远程通信的依赖性的基本问题。如果事务操作需要向外调用并且告诉其他调用者某些操作正在进行,那么“原子性”如何成为事务的操作?如果决定需要客户通知方案,请考虑使通知异步,或者将事务分割为两个独立的阶段。
事件同时带来了与 DCOM
安全配置有关的问题(调用方向已经逆转,因此它可能需要对客户的安全配置进行改动,或者修改防火墙配置),并且带来了在建立连接点回调时所需的额外往返而引起的性能问题。
随着应用程序的发展,您会发现在几个机器之间划分功能的必要性。对于最初仅仅在一台机器上经过测试并支持应用程序的方法来说,使用这些方法时要小心。在不进行大量修改的情况下,可能没有使您今后分布应用程序的灵活性。
操作方式
确保不在 COM+ 对象中使用 RaiseEvent。如果需要就某个事件向许多未知服务器对象发出通知,那么可以使用
COM+ Events
进行评估。对于异步通信,请查看排队组件、MSMQ
或某些其他的通知方案。
|