多线程和异步事件处理是设计高并发和高性能服务器程序的主要技术,但很少有应用把两种技术组合在一起使用,原因是二者的组合会增加系统的复杂度,并使代码难以理解。多线程程序可以充分利用现代处理器多核的处理能力,使一个进程的多个任务可以并行执行,提高程序执行的效率。但多线程程序也有其不可回避的缺点。资源竞争导致的死锁,线程调度和切换带来的开销,限制了服务器程序处理连接的能力。异步事件处理程序避免了多线程的资源竞争和死锁,它通过事件循环来调度和执行代码,可以使服务器程序处理大量的连接。但异步处理程序只支持单核;一个事件处理函数处理过多事件会导致处理的延迟;对CPU密集型事件的处理会导致挂起事件的饥饿。如果在代码复杂度可控的情况下,很好地组合这两种技术,取其长而避其短,就能设计出高性能和高并发的服务器程序。Traffic Server多线程异步事件处理程序就是这类应用。

Traffic Server并不是为每个连接都建立一个线程,而是事先创建一组数量可配置的工作线程,每一个工作线程上都运行着独立的异步事件处理程序。异步事件处理程序是通过状态机模型来实现的。状态机模型具有很强的描述系统行为的能力,尤其是针对具有事件驱动的并发的特征的问题,用状态机来建模非常合适。将状态机作为一种构建系统的基本模块来对系统进行分解,将会使很多原本复杂的问题简单化。一个状态机(state machine)是一个行为,它说明对象在生命周期中响应事件所经历的状态序列以及对事件的响应。一个状态(state)是指在对象生命周期中的一个条件或状况,在此期间对象将满足某些条件、执行某些状态或等待某些事件。一个事件(event)是对一个在时间和空间上占有一定地位的有意义的事情的规格说明。在状态机中,一个事件能够触发一个状态转换。一个转换(transition)是两个状态之间的一种关系,它指明对象在第一个状态中执行一定的动作,并当特定事件发生或特定的条件满足时进入第二个状态。一个动作(action)是一个引起模型状态改变或值的返回的可执行的原子操作?[1]

Traffic Server对多线程异步事件处理模型的抽象如图1所示。EventProcessor类是模型的核心组件。它负责创建和管理若干组thread,这些thread用于在给定时间内或周期性地异步执行用户定义的任务。EventProcessor提供了一组调度函数,通过这些调度函数可以指定它的一个线程来回调continuation。这些函数调用是非阻塞的。相反,它们返回一个Event对象,并在随后或一个特定时间安排contination的回调。Event由EventProcessor来分配和释放。状态机可以访问返回的,非递归的事件,直到事件被取消或事件的回调函数完成。对于递归的event,event只有在被取消之后才会被访问。对event完成或被取消,eventProcessor负责释放它。

Ethread类是由Event系统创建和管理的thread类型。它在event系统中是一个可以用来调度event的接口(其他两个是Event和EventProcessor类)。为了处理event,每个Ethread对象有两个event队列,一个外部和一个内部。外部队列为Ethread的用户提供,并通过它来附加event到该特定thread。外部队列可以同时被别的thread访问,对它的任何操作都必须以原子方式进行处理。另一方面,内部队列只由Ethread使用,用来处理一定时间内的定时event。这些event在内部排队,也可以来自外部队列。

Event类是Action的一种,作为调度一个操作的结果由EventProcessor返回。与代表异步操作的action不同的是,event是不能可重入调用的。除了可以取消一个event(因为它是一个action)之外,还可以在接收时重新调度它。当通过Event类的调度函数重新调度一个event时,状态机必须在回调它们的线程而非其他线程中调用这些函数。在调用任何调度函数之前,必须已经获得了continuation的锁。取消event的规则和其他的action是一样的:一个event的取消者必须是可以通过任务回调的状态机,在取消操作进行时,状态机的锁必须是被占用的。状态机拥有的任何对event对象的引用(比如指针)在取消之后都不能被使用。

Continuation类是一种抽象机制,用来和它的使用者通信事件的发生,在整个IO core 事件系统中都被用到。Continuation是一个轻量级的数据结构,它实现了一个单一的方法来供使用者回调。通常通过子类化Continuation来实现事件驱动的状态机。通过包含附加的状态和方法,continuation可以结合状态和控制流,并通常用来支持分阶段,事件驱动的控制流。鉴于Event系统对线程的特性,每一个continuation对携带一个ProxyMutex的引用来保护其状态并确保原子操作。这个ProxyMutex对象必须由continuation驱动的类或者IO core Event系统的客户来分配,并且要作为continuation类构造函数的一个参数。

总结一下:Processor创建若干组Thread,并将Event按类型调度到相应的Thread的Event队列上,Thread通过执行Event对应的Continuation中的回调函数,来完成状态的迁移。从初始态到终止态的迁移代表了整个事件的执行过程,而Thread是永不退出的,等待着下一个事件的到来。

以上是笔者分析Traffic Server iocore中的event system后的一些见解,有不当之处,欢迎大家指正。

参考文献:

[1] Grady Booch, James Rumbaugh, Ivar Jacobson. Unified Modeling Language User Guide, The (2nd Edition).