昨天说到了,K.O.(KeyFC Open Translation Toolset)采用了代号为Chobits Application Server(CAS)的架构。
今天谈一下CAS的内部结构。
--- 消息队列 ---
CAS区别于一般模块化设计的特点就是,模块之间的关系是消息传递而不是控制传递。
既然是消息传递,队列是不可少的了。
每一个CAS服务模块至少带有一个消息队列:
服务请求从其他模块发送到此队列中;此模块的服务线程获取请求,执行处理并且根据情况作出回复;回复仍然是以消息形式,从此模块发送到其他模块的队列中;
(图1)
前面提到,CAS能够使程序员(基本上)用传统的单线程程序思路写出很容易的稳定的多线程的应用程序,消息队列在其中扮演了重要的角色。
消息队列就像一个"同步防火墙",将绝大多数的多线程同步操作隔离在服务模块代码之外;交给模块编程者的,是一个简洁,稳定的单线程环境。
因为需要被多个线程同时访问,消息队列本身需要对其所有操作进行线程同步(Thread Synchronization)以避免“竞态条件”(Race Condition)...
经典的做法是使用操作系统提供的同步对象,比如互斥(Mutex)、信号灯(Semaphore)、临界区(CriticalSection)等...
但是由此带来的问题是程序执行效率的降低,特别是在多处理的环境下,随着处理器数量的增多,同步效率急剧下降...
但是,我们可以注意到CAS消息队列本身结构很简单:
相对于传统的Grid Computing中的MPI消息传递,CAS有一个绝对的优势,就是模块之间内存可以共享(因为处于同一个进程中)。
这样CAS消息传递本身也就能够被极大的优化——所谓一条消息,其实也就是一个指针、4个字节而已(x86结构下)。
因此,实现消息队列也就可以应用另一项同步技术——免锁同步(Lock-free Synchronization)。
免锁同步相对于传统技术有很多优势,主要两点:在多处理器环境同步效率高;无死锁问题 (因为根本就没有用锁=v=)。
具体技术细节,请使用Google学术搜索查找相关论文。
http://scholar.google.com经过实际测试(4生产线程+4消费线程),
在单处理器环境下,免锁CAS消息队列 基本与 临界区同步CAS消息队列 性能相当;
双处理器,免锁队列同步效率要高5%—30%;
四处理器,免锁队列同步效率要高80%—300%;
(如谁有八个或更多处理器的机器,欢迎联系我获取测试程序!^_^)
--- 消息接口 与 消息总线 ---
前面提到了模块之间依靠相互交换消息来完成一系列的操作。
那么,聪明的各位一定会问:一个模块怎么知道另一个模块的消息队列在什么地方,又怎么去传递消息呢?
回忆一下,我一开始就提到,CAS架构的宗旨就是“极大模块化”程序的设计和实现。
对于应用模块编写者来说,CAS消息机制本身就是一套与应用模块无关的功能;
消息相关的功能可以完全的模块化出来,"眼不见心不烦"。
因此,在实际的CAS架构下,模块是看不到消息队列的,不论是自己的还是其他模块的;看得到的只是消息接口(Interface)。
消息接口提供给模块一套基本的功能:获取自己消息队列的消息,查询其他模块的接口编号,发送一个封装好的消息(消息中包含接受方的接口编号)。
至于消息队列的管理以及消息的传递是如何进行的,统统不用操心,一切都由这个消息模块完成。
这个消息模块,就是消息总线(Message Bus)。
一个模块载入时,需要提供一个模块名称和一个GUID(唯一序列号)给消息总线,消息总线为这个模块注册一个消息队列,并将接口返回给该模块;
于是,这个模块就可以利用消息接口向总线上其他已注册的模块发送消息,其他的模块也可以通过总线查询到这个模块的接口编号...
(图2)
需要提起的是,消息总线提供给模块两种发送消息的方法:传送和投递。
传送是由发送方的线程直接将消息放入接受方的队列中;而投递则是将消息交给总线,由总线调度将消息传送给接受方。
两种方法各有所长:
* 投递 的优点是消息发送速度快,适合需要向很多模块发送大量消息的场合(比如媒体文件的播放 / 动画特效的渲染);但是当接受方的队列阻塞(一定时间)时,消息将会被自动释放,因此不适合要求可靠传递的消息(向一个文件写数据)。
* 传送 则保证消息投放入接受方的队列,如果有任何原因无法传送,发送方肯定会得知,并且可以按预定动作处理这一事件。
------
今天谈的这一些是CAS的基础的结构,下次我将谈谈这个结构如何工作:CAS消息的传递 (仍然是自然语言描述,不涉及具体API)。