现在的位置: 首页 > 综合 > 正文

状态模式在服务器中的应用

2013年12月07日 ⁄ 综合 ⁄ 共 3456字 ⁄ 字号 评论关闭

不只一次听到不只一个人说,设计模式就是那么回事,怎么怎么回事,那么那么回事,大致都带着肯定的口吻——表达的不是设计模式如何而是听着应该屈从于说者的权威,不容置疑的自信,也带着那么丝不确定。

我觉得设计模式是软件行业很不错的书,而软件行业的好书基本上都是国外出版,好书就是丰富经验的积累和说明,讲着故事和经历,阐明原理和规律。所以我更关心我如何理解设计模式,如何应用设计模式。

最初那段时间,完全是设计模式的粉丝,非模式不代码,特别是新的软件无论大小,都要往模式上靠——就像OOAD的作者说的,拿着钉锤的人眼中世界全是钉子,总想敲敲打打一番——最恰当不过了。

设计模式的基础是OO,流传着这样的观点:大部分的C++开发者,用C++写C代码(C++之父),以及OOAD作者说的“给一个不懂得如何使用电钻的家伙,他可能会拿它当钉锤——结果当然是不那么好用,而且大多会砸到自己的手指头”。所以,我发誓,要好好学习OO和C++。

认知是层次性的,而且会不断反复。状态模式很早就想写,但又没有什么好写的,终于有两三个满意的软件用状态模式解决服务器状态问题,我想,要总结一下,给自己以后使用这个模式时更快进入角色,也更方便给别人讲解。

服务器必然有状态,有服务器本身的状态,还有客户端的状态。当状态简单时,就不必用模式了,譬如1万行的简单服务器,这个复杂度OO都不用,直接用几十个函数和变量表达就OK了。但对于稍复杂的状态,状态模式大有可为。

先总结如下:

1. 复杂状态变换,才需要使用状态模式。否则就直接用变量表示吧。

2. 只要服务器的状态能分析清楚,状态模式就一定能解决问题。即只要状态可知,就能在预期范围内变化(即实现功能)。

3. 状态模式可以稍作修改:状态模式中,改变状态的动作是一个函数,对于很多状态而言,函数和类都会剧增,所以可以用一个函数,动作用参数表示。

4. 状态模式应关注的是当前状态改变为其他状态时的情况,而不要关注当前状态的上一状态是什么(实际上是等同的,但前者会简单,或者很复杂)。

考虑典型的多进程模型(http://blog.csdn.net/winlinvip/article/details/7764526),若系统有三个状态:ready,reading,stopping,在master中管理状态:

// master process
block_all_signals()

for(;;){
    sigsuspend(); // get a signal.
    if(state = reading){
        do_read();
    }
    else if(state = stopping){
        exit(0);
    }
    else{
        // ready.
    }
}

void signal_handler(...){
    if(READ){
        state = readding;
    }
    else if(STOP){
        state = stopping;
    }
    else{
        state = ready;
    }
}

用state变量表示当前的状态,没有状态转换的概念(没有上下文)。

若使用修改过的状态模式表示如下:

// master process
block_all_signals();

for(;;){
    sigsuspend(); // get a signal
    state->do(); // state work
}

// state change
void signal_handler(...){
    if(READ){
        state->action(READ);
    }
    else if(STOP){
        state->action(STOP);
    }
}

class ready_state{
    void action(command){
        // ready=>reading
        if(command = READ){
            set_state(reading_state);
        }
        // ready=>stopping
        else if(command == STOP){
            set_state(stopping_state);
        }
    }
}
class reading_state{
    void do(){
        //do reading....

        // change to ready
        set_state(ready_state);
    }
}
class stopping_state{
    void do(){
        exit(0);
    }
}

状态模式实际上在state中保存了当前的状态,在signal_handler中进行状态转换,在for(;;)主循环中执行状态应该做的事情(避免在signal_handler中做过多操作)。实际上它实现了以下状态转换图:

init => ready => reading => init
                       => stopping => exit

状态转换的意义在哪里?在于定义了可能的状态图,即有很多assert,当系统异常时就会触碰这些assert从而告知我们准确的发生了什么不应该发生的事情。而且转换图实际上清晰定义了系统的状态:只有熟知的系统才能很好的实现,只有清晰的定义才能表明我们已“熟知”此系统,不清晰的定义即模模糊糊的边界,在复杂系统中就是一种灾难。

举个稍复杂的转码服务器状态变换以及状态模式实现的例子,系统使用单线程实现,轮流处理多个任务,包括以下状态:

  1. ready:任务的准备状态。
  2. transcoding:正在转码。
  3. reporting:转码任务完毕汇报。
  4. error:转码任务出错。
  5. canceling:正在取消任务。
  6. removing:正在删除任务。
  7. finishing:任务转码完毕。

这些状态和每个任务相关,轮流处理任务时,从任务的状态开始转换。状态转换图:

主循环代码:

for(i=0; i < task_nums; i++){
    task = get_task(i);
    if(task.state.do()){ // completed.
        remove_task(task);
    }
}

class read_state{
    bool do(){
        if(cancel){
            return set_state(cancel_state);
        }
        if(remove){
            return set_state(remove_state);
        }
        return set_state(transcoding_state);
    }
}

class transcoding_state{
    bool do(){
        // do transcoding.....

        if(cancel){
            return set_state(cancel_state);
        }
        if(transcode_ok){
            return set_state(reporting_state);
        }
        if(transcode_error){
            return set_state(error_state);
        }
    }
}

class canceling_state{
    bool do(){
        // do cancelling...

        return true;
    }
}

class error_state{
    bool do(){
        // do error cleanup...

        return true;
    }
}

class removing_state{
    bool do(){
        // remove task resource...

        if(remove_error){
            return set_state(error_state);
        }
        return true;
    }
}

class reporting_state{
    bool do(){
        // do reporting...

        if(cancel){
            return set_state(cancel_state);
        }
        if(report_error){
            return set_state(error_state);
        }
        return set_state(finishing_state);
    }
}

class finishing_state{
    bool do(){
        // do finishing...

        return true;
    }
}

若使用状态变量来表达状态转换,会不直观,而且比较麻烦了。

若使用多进程模型,master在sigsuspend可能会收到多个信号,而且一般在信号处理函数中只是改变状态,在主循环中action。此时用state模式更加自然和处理完善,因为此时的模型会更复杂。

几个理解:

  1. 状态模式在表达异步状态时表现优越,异步状态本来就是包含了较长状态的含义,相反非异步操作用函数表达就可以了。
  2. 状态模式同样可以表达网状的状态转换,这在表达复杂的步骤时很有用,譬如A的下一步能到C或D,C下一步是D或E,D下一步是A或C,这样复杂的路径转换。
  3. 状态模式必须要有归属,即表达谁的状态,是谁的状态。若很模糊,可能没有抽象好,或者根本就不应该用状态来表达。

抱歉!评论已关闭.