全文来源:
http://www.cppblog.com/darkdestiny/1.服务器间的异步事务上个月,最有收获的事情,大概就是这个了吧。
几个不同功能的服务器相互协作以实现一个角色登录场景的需求。这是一个异步的过程,异步令人讨厌的地方就是,他不是一个服务器能够顺序完成的事情,而你却不知道什么时候什么服务器会因为太阳风的异常活动而崩溃。如果你假设所有服务器都是稳定的,他们最多只有80%的时间如你所愿,剩下的就是boss的咆哮(2/8原则)。
一开始我并不了解异步事务的概念,所以角色登录场景的流程写得非常松散。用A,B,C,D合作烤苹果比方的话,就是
*A抓起一个苹果,交给B
*B把苹果切开,交给C
*C在苹果片抹上胡椒,交给D
*D把苹果烤熟
以人类的眼光看这个流程,是一个完美的流水线,而且中途谁因为天热而蒸发掉的话,大家也都很清楚的知道而作出恰当的反馈(暂停流水线,等待信春哥的某人满血满buff原地复活等等)。
但是这样的流程却完全不适合映射到程序上——多个服务器之间对彼此行为和状态的详细跟踪,是一件多么困难,复杂,不通用,吃力不讨好的事情。那么在程序上,烤苹果的工人之间就不是在一个大开间畅聊,而是被关在一个小黑屋里孤独的工作,彼此不能看到,并且传递苹果的空间非常不稳定,工人必须时刻用一只手等苹果,否则苹果就会变成香蕉。工头Master(M)为节省成本没有开空调,所以偶尔有人蒸发又复活什么的,但是流水线不会停下来,100个苹果也就烤出80个左右。
M觉得亏20个苹果没什么,但是因为吃不上苹果而流失的顾客是一个极大的损失。M决定改进流水线,亲自参加烤苹果。
*顾客想吃烤苹果,M向A索要苹果
*A抓起一个苹果,交给M
*M检查苹果,把苹果交给B
*B把苹果切开,交给M
*M把苹果片交给C
*C在苹果片抹上胡椒,交给M
*M把苹果交给D
*D把苹果烤熟,交给M
此时,M把热腾腾的烤苹果交给了顾客,顾客因为热腾腾引起了不好的联想,走掉了。
这一次,M对顾客要吃的每一个苹果都贴上一个唯一标签,设置一个5秒超时,如果超时则重新为顾客烤苹果,原来烤了一半的直接扔掉。
前面铺垫了那么多,其实就是想说明一件事情:
处理服务器间的异步事务,最好设置一个master服务器,集中控制每一个步骤的运作,为每一个事务设置一个超时;在事务失败或者超时后作出反馈。2.异步事务的实现方式2.1状态机
第一次我是用状态机实现的,master等待每个worker反馈的过程作为一个状态,worker的反馈作为一个trigger,迫使master跳转到下个事务状态,执行step,接着等待新的反馈,直到事务结束。
基于状态机的形式听起来很直观,但实际上用起来并非如此顺手,总是觉得有些违和,修改的时候总会遭遇一些复杂情景,特别是在中间修改事务步骤的时候。
其中我所犯的一个错误就是给予每一个步骤设置设置步骤超时的机制,这非常不好做,容易出bug,最后才意识到一个事务一个超时的规则。
2.2C协程
协程可以认为是需要手动进行调度的线程,协程不像线程那样由OS调度,协程何时运行何时挂起都是由程序员控制的。
C的体系下是没有协程的,虽然找到了一些C协程的库,可是接口和实现都相当不雅。唯一一个我认可的,是一个基于少量多线程来实现协程功能的库,可是在服务器上使用多线程是谨慎事项之一。
其他语言体系下的同学,如果有好的协程支持(最好是基于多堆栈的),大可放心使用。
2.3lua协程
这是最后实现并决定的方式,如果今后不改变的话。lua支持多堆栈,所以lua有协程是理所当然的了。
用协程实现事务有哪些好处呢?可以将整个异步事务的代码写在一个函数中,就像一个程序内的同步操作一样,这是一件多么美好而具有可读性的事情,相比状态机而言。
如何用协程实现事务呢?执行一个事务时,协程函数运行,执行step,然后在等待反馈的时候挂起;master收到反馈以后,resume协程,在上次挂起的地方继续往下运行,执行下一个step,等待新反馈而挂起;如此反复,直至完成。
posted on 2009-07-05 18:46
LOGOS 阅读(5100)
评论(8) 编辑 收藏 引用 所属分类:
month-flow