Memcached源码分析——连接状态变化分析(drive_machine)

这篇文章主要介绍Memcached中,基于libevent构造的主线程和worker线程所处理连接的状态互相转换的过程(不涉数据的存取等操作),也就是drive_machine的主要业务逻辑了。状态转换过程没有涉及所有状态,同时,由于自己能力问题,一些状态转换还可能有错,还请各位前辈指正。转换条件限定:TCP,ASCII协议。(状态不包括conn_swallow,二进制协议才会用到此状态)

1 总揽

首先介绍连接,连接是Memcached自己定义的连接conn。的所有状态,drive_machine()主要就是对这些状态的转换进行操作:

enum conn_states {
    conn_listening,  /**< the socket which listens for connections /主线程等待链接*/
    conn_new_cmd,    /**< Prepare connection for next command /worker线程等待命令*/
    conn_waiting,    /**< waiting for a readable socket /worker线程等待sdf中有可读信息*/
    conn_read,       /**< reading in a command line /worker线程读命令*/
    conn_parse_cmd,  /**< try to parse a command from the input buffer /woker线程*/
    conn_write,      /**< writing out a simple response /worker线程*/
    conn_nread,      /**< reading in a fixed number of bytes /worker线程由于命令没读完,继续读取*/
    conn_swallow,    /**< swallowing unnecessary bytes w/o storing /worker线程,ASCII协议不涉及此状态*/
    conn_closing,    /**< closing this connection */
    conn_mwrite,     /**< writing out many items sequentially /worker线程,响应内容中写出*/
    conn_max_state   /**< Max state value (used for assertion) /用于标记conn_states边界,超出说明states有错*/
};

在Memcached.c的main()中,主线程以及worker线程完成必要初始化,绑定相应libevent事件,随后主线程监听绑定监听相应端口,正式做好循环的准备。下图是总体连接状态的总体转换图。

2 接收连接分发给worker线程

 

  1. 主线程初始化后,连接便进入conn_listening状态,等待连接。
  2. 当有连接连入,主线程通过dispatch_conn_new()向管道写入字符c,将连接分发给某个worker线程,worker线程收到,连接进入conn_new_cmd等待状态。

3 数据读入

  1. conn_new_cmd等待新的连接,当连接写入时,如果当前请求过多,可能对其他worker线程造成饥饿,更改事件类型为EV_WRITE,退出。
  2. 如果连接不多,且有未读数据,说明上一次有未处理完的命令,进入conn_parse_cmd,以便继续处理命令。
  3. 如果连接不多,且是新连接,表明没有未读数据,则连接进入conn_waiting状态等待数据(命令)到来。
  4. conn_waiting,线程等待连接中的后续数据,如果有数据会设置当前socket fd为读事件EV_READ,确保后续内容的读入。在conn_read中,调用try_read_network()尝试从socket fd中读东西,如果没有数据则返回conn_waiting,如果有数据则进入conn_parse_cmd,等待处理命令。

4 命令解析

  1. 进入conn_parse_cmd后,收到命令,调用try_read_command()对用户命令进行解析,这里分析ASCII协议命令,其中会调用process_command()来处理用户的命令。
  2. 根据用户的命令,会调用不同类型的命令。如set,replace等命令对应update_command();incr,decr对应process_arithmetic_command();gets对应process_get_command()等。
  3. 如果是更新类型的命令,则还需要读入更新的内容,进入conn_nread状态。
  4. 其他类型的命令已经可以等待返回结果了。

5 命令返回

  1. 命令执行后会有响应,如果命令只需要反馈一个执行结果,则通过out_string()函数来输出结果,进入conn_write阶段。
  2. 如果是get命令,则需要打印get的内容,则进入conn_mwrite阶段。
  3. conn_write阶段的case语句没有break,所以正常打印也会进入conn_mwrite。

6 结果输出

  1. conn_mwrite会中,会调用transmit()将响应结果进行输出。transmit()检查当前结果的写入状态。
  2. 如果数据写完,则改变状态为conn_new_cmd等待新的命令。
  3. 如果没有写完,则继续等待,直到写完。

7 小结

以上就是整个连接状态的转换情况了,分析不到位或者有误的地方还请大家指正。总体来说,感觉Memcached对libevent的应用写的十分标准,后续使用libevent时,也可以从Memcached中学习到很多知识。

posted @ 2014-03-20 15:36  诶露基基(LGG)  阅读(1128)  评论(0编辑  收藏  举报