一、并发和网络化对象
1.动机
硬件发展的快速与软件发展的缓慢之间的不匹配有许多因素:
1)固有的和偶发的复杂性。固有的复杂性来源于基本的领域难题,例如处理部分失效、分布式死锁和端到端的服务质量(QoS)需求。偶发的复杂性来源于软件工具和开发技术的局限,例如不可移植的编程API和拙劣的分布式调试器。
2)不适当的方法和技术。流行的软件分析方法和设计技术主要集中于构造单进程、单线程应用。而高质量的并发和网络化系统的开发,则要靠熟练的软件体系结构设计者和工程师的直觉和专门知识。
3)不断地重复发明和重新发现核心概念和技术。
模式是一个在特殊的语境下,对一个标准问题的重现的解决方案,模式有助于在软件设计中捕捉和复用静态和动态结构以及关键参与者之间的协作。尤其是模式能够做下列工作:
1)使用开发人员将精力集中于更高级的软件应用体系结构和设计,例如合适的服务访问与配置、事件处理和线程模型的规范。这些是并发和网络化软件的有战略意义的重要方面。
2)再次避免开发人员专注于低层操作系统及网络协议和机制。
2.并发和网络化软件的难题
开发人员必须面临独立应用不涉及到或不太成为问题的主题包括:
1)建立连接和初始化服务。
2)事件多路分解和事件处理程序分配。
3)进程间通信(IPC)和网络协议。
4)主存储器和次级存储器管理和缓存。
5)静态的和动态的组件配置。
6)并发和同步。
在这些主题的语境中,会出现许多设计和编程难题:
1)与并发和网络化系统有关的常见的固有的复杂性包括管理带宽、最小化延迟和延迟偏差(抖动)、检测部分失效并从中恢复、确定合适的服务划分和负载平衡策略、确保事件的因果顺序。类似地,在并发编程中含有的常用固有的复杂性包括消除竞争条件和避免死锁、确定适合的线程调度策略、优化终端系统协议处理性能。
2)与并发和网络化系统相关的,常见的偶发复杂性包括可移植性操作系统API的缺乏、不充分的调试支持和用于分析并发和网络化应用程序的工具的缺乏、广泛的算法的分解、不断的重新发现和重复发明核心概念和常用组件。
2.1服务访问和配置
独立应用程序中的组件可以借助于函数调用传递参数和访问全局变量,在一个单一的地址空间范围内协作。在网络化应用程序中,组件可以使用下列手段进行协作:
1)进程间通信(IPC)机制,例如共享内存、管道和套接字。
2)通信协议,例如TELNET、FTP、SMTP、HTTP和LDAP。
3)使用高级通信中间件,例如COM+和CORBA,在应用级服务组件上进行远程操作。
对于基础设施连网或者系统程序(如TELNET或FTP)而言,传统上的服务访问包括调用下列API:
1)调用并发服务访问API来管理并发,如UNIX进程、POSIX Pthreads或者Win32线程。
2)调用IPC服务访问API,例如UNIX和互联网领域套接字,配置位于单一主机或不同主机上的进程之间的连接和通信。
然而,当通过低层操作系统C语言API访问连网和主机服务时,会出现几个偶发的复杂性:
1)过度的低层细节。
2)不兼容的高级编程抽象的不断重新发现和重复发明。
3)出错的高可能性。
4)缺乏可移植性。
5)陡峭的学习曲线。
6)不能按比例扩展以处理不断增长的复杂性。
因此,基础设施连网或者系统程序的最主要的设计难题,是在不牺牲性能的前提下,最小化上述偶发的复杂性。
支持服务和应用程序的静态和动态演化,演化能够以两种方式出现:
1)通常可以在运行时改变对组件服务角色的接口和组件服务角色之间的互连,新的服务角色可以实现并安装到现有的组件中。
2)通过重新配置服务负载以利用多主机的处理能力,可以提高分布式系统性能。
理想情况下,这些组件配置和重配置变化对于访问服务的客户机应用程序而言应该是透明的。但更具挑战性的是确定如何访问被“按需”配置到系统中的服务,当系统最初设计时,此服务的实现是未知的。
许多现代操作系统和运行时环境提供显式的动态链接API,允许按需求配置应用程序。然而,按需求把服务配置到应用中需要的不仅是动态的链接机制——它需要用于协调(重新)配置策略(Policy)模式。这里的设计难题是两方面的。首先,即使应用程序也许不知道它们的详细接口,它也必须导出新的服务;其次,应用程序即使在运行时也必须把这些服务透明地,健壮地集成到它自己的控制流和处理序列中。
在独立的和网络化的软件系统和应用程序中,用于设计访问并配置服务和组件的有效的编程API的四种模式,是包装器外观(Wrapper Facade)、组件配置器(Component Configurator)、截取器(Interceptor)和扩展接口(Extension Interface)。
2.2事件处理
随着系统日益变得网络化,支持事件驱动应用程序的软件开发技术也变得越来越普遍。以下三个特性使事件驱动的应用程序有别于具有传统的“自定向”控制流的那些应用程序:
1)应用程序行为被异步出现的外部或者内部事件触发。这些事件包括设备驱动程序、I/O端口、传感器、键盘或者鼠标、信号、定时器或者其他的异步软件组件。
2)大多数事件必须得到迅速的处理,以防止CPU资源匮乏,改进可察觉的响应时间,使具有实时约束的硬件避开失效的或被破坏的数据。
3)也许需要有限状态机来控制事件处理并检测非法的转换,因为事件驱动的应用程序通常只有很少或者没有对事件到达次序的控制。
因此,事件驱动的应用程序常常构造为通常所说的“控制转换”(inversion of control)的分层的体系结构。
1)底层是事件源(event source),它从各种硬件设备或者驻留在操作系统中的低层软件设备驱动程序那里检测和检索事件。
2)下一层是事件多路分解器(event demultiplexer),它使用类似select()这样的函数,在各种各样的事件源上等待事件的列表,接着把事件分配给它们对应的事件处理程序回调。
3)事件处理程序(event handler)和应用程序代码一起,又形成另一层,它执行响应回调的与应用有关的处理——因此有“控制转换”这一名称。
这些模式描述了如何在网络化软件框架中有效地初始化、接收、多路分解、分配和处理各种类型的事件。包括有反应器(Reactor)、主动器(Proactor)、异步完成标记(Asynchronous Completion Token,ACT)和接受器——连接器(Acceptor-Connector)。