Memcached源码阅读2

Memcached源码阅读之线程交互

Memcached按之前的分析可以知道,其是典型的Master-Worker线程模型,这种模型很典型,其工作模型是Master绑定端口,监听网络连接,接受网络连接之后,通过线程间通信来唤醒Worker线程,Worker线程已经连接的描述符执行读写操作,这种模型简化了整个通信模型,下面分析下这个过程。

[cpp]  view plain  copy
 
  1. case conn_listening:  
  2.         addrlen = sizeof(addr);  
  3.                 //Master线程(main)进入状态机之后执行accept操作,这个操作也是非阻塞的。  
  4.                 if ((sfd = accept(c->sfd, (struct sockaddr *) &addr, &addrlen)) == -1)  
  5.         {  
  6.             //非阻塞模型,这个错误码继续等待  
  7.                         if (errno == EAGAIN || errno == EWOULDBLOCK)  
  8.             {  
  9.                 stop = true;  
  10.             }  
  11.                         //连接超载  
  12.                         else if (errno == EMFILE)  
  13.             {  
  14.                 if (settings.verbose > 0)  
  15.                     fprintf(stderr, "Too many open connections\n");  
  16.                 accept_new_conns(false);  
  17.                 stop = true;  
  18.             }  
  19.             else  
  20.             {  
  21.                 perror("accept()");  
  22.                 stop = true;  
  23.             }  
  24.             break;  
  25.         }  
  26.                 //已经accept成功,将accept之后的描述符设置为非阻塞的  
  27.                 if ((flags = fcntl(sfd, F_GETFL, 0)) < 0  
  28.                 || fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0)  
  29.         {  
  30.             perror("setting O_NONBLOCK");  
  31.             close(sfd);  
  32.             break;  
  33.         }  
  34.                 //判断是否超过最大连接数  
  35.         if (settings.maxconns_fast  
  36.                 && stats.curr_conns + stats.reserved_fds  
  37.                         >= settings.maxconns - 1)  
  38.         {  
  39.             str = "ERROR Too many open connections\r\n";  
  40.             res = write(sfd, str, strlen(str));  
  41.             close(sfd);  
  42.             STATS_LOCK();  
  43.             stats.rejected_conns++;  
  44.             STATS_UNLOCK();  
  45.         }  
  46.         else  
  47.         {       //直线连接分发     
  48.                         dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST,  
  49.             DATA_BUFFER_SIZE, tcp_transport);  
  50.         }  
  51.   
  52.         stop = true;  
  53.         break;  
这个是TCP的连接建立过程,由于UDP不需要建立连接,所以直接分发给Worker线程,让Worker线程进行读写操作,而TCP在建立连接之后,也执行连接分发(和UDP的一样),下面看看dispatch_conn_new内部是如何进行链接分发的。

[cpp]  view plain  copy
 
  1. void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags,  
  2.                        int read_buffer_size, enum network_transport transport) {  
  3.     CQ_ITEM *item = cqi_new();//创建一个连接队列  
  4.     char buf[1];  
  5.     int tid = (last_thread + 1) % settings.num_threads;//通过round-robin算法选择一个线程  
  6.   
  7.     LIBEVENT_THREAD *thread = threads + tid;//thread数组存储了所有的工作线程  
  8.   
  9.     last_thread = tid;//缓存这次的线程编号,下次待用  
  10.   
  11.     item->sfd = sfd;//sfd表示accept之后的描述符  
  12.     item->init_state = init_state;  
  13.     item->event_flags = event_flags;  
  14.     item->read_buffer_size = read_buffer_size;  
  15.     item->transport = transport;  
  16.   
  17.     cq_push(thread->new_conn_queue, item);//投递item信息到Worker线程的工作队列中  
  18.   
  19.     MEMCACHED_CONN_DISPATCH(sfd, thread->thread_id);  
  20.     buf[0] = 'c';  
  21.     //在Worker线程的notify_send_fd写入字符c,表示有连接     
  22.     if (write(thread->notify_send_fd, buf, 1) != 1) {  
  23.         perror("Writing to thread notify pipe");  
  24.     }  
  25. }  

投递到子线程的连接队列之后,同时,通过忘子线程的PIPE管道写入字符c来,下面我们看看子线程是如何处理的?

[cpp]  view plain  copy
 
  1. //子线程会在PIPE管道读上面建立libevent事件,事件回调函数是thread_libevent_process  
  2. event_set(&me->notify_event, me->notify_receive_fd,  
  3.               EV_READ | EV_PERSIST, thread_libevent_process, me);  
  4.   
  5. static void thread_libevent_process(int fd, short which, void *arg) {  
  6.     LIBEVENT_THREAD *me = arg;  
  7.     CQ_ITEM *item;  
  8.     char buf[1];  
  9.   
  10.     if (read(fd, buf, 1) != 1)//PIPE管道读取一个字节的数据  
  11.         if (settings.verbose > 0)  
  12.             fprintf(stderr, "Can't read from libevent pipe\n");  
  13.   
  14.     switch (buf[0]) {  
  15.     case 'c'://如果是c,则处理网络连接  
  16.     item = cq_pop(me->new_conn_queue);//从连接队列读出Master线程投递的消息  
  17.   
  18.     if (NULL != item) {  
  19.         conn *c = conn_new(item->sfd, item->init_state, item->event_flags,  
  20.                            item->read_buffer_size, item->transport, me->base);//创建连接  
  21.         if (c == NULL) {  
  22.             if (IS_UDP(item->transport)) {  
  23.                 fprintf(stderr, "Can't listen for events on UDP socket\n");  
  24.                 exit(1);  
  25.             } else {  
  26.                 if (settings.verbose > 0) {  
  27.                     fprintf(stderr, "Can't listen for events on fd %d\n",  
  28.                         item->sfd);  
  29.                 }  
  30.                 close(item->sfd);  
  31.             }  
  32.         } else {  
  33.             c->thread = me;  
  34.         }  
  35.         cqi_free(item);  
  36.     }  
  37.         break;  
  38.     }  
  39. }  
之前分析过conn_new的执行流程,conn_new里面会建立sfd的网络监听libevent事件,事件回调函数为event_handler。

[cpp]  view plain  copy
 
  1. event_set(&c->event, sfd, event_flags, event_handler, (void *) c);  
  2.     event_base_set(base, &c->event);  

而event_handler的执行流程最终会进入到业务处理的状态机中,关于状态机,后续分析。


Memcached源码分析之状态机(一)

按我们之前的描述,Master线程建立连接之后,分发给Worker线程,而Worker线程处理业务逻辑时,会进入状态机,状态机按不同的状态处理业务逻辑,我们在分析连接分发时,已经看到了Master线程进入状态机时在有新连接建立的时候,后续的状态都是业务逻辑的状态,其处理流程如下图所示:

Memcached源码阅读2_第1张图片

总共有10个状态(代码中的状态不止这些,有些没什么用,此处就没展现),状态listenning状态是Master建立连接的过程,我们已经分析过了,我们接下来分不同的文章分析其余的9中状态。

[cpp]  view plain  copy
 
  1. enum conn_states {  
  2.     conn_listening,  //监听状态  
  3.     conn_new_cmd,    //为新连接做一些准备  
  4.     conn_waiting,    //等待读取一个数据包  
  5.     conn_read,       //读取网络数据  
  6.     conn_parse_cmd,  //解析缓冲区的数据  
  7.     conn_write,      //简单的回复数据  
  8.     conn_nread,      //读取固定数据的网络数据  
  9.     conn_swallow,    //处理不需要的写缓冲区的数据  
  10.     conn_closing,    //关闭连接  
  11.     conn_mwrite,     //顺序的写多个item数据  
  12.     conn_max_state   //最大状态,做断言使用  
  13. };  

这篇文件先分析conn_new_cmd和conn_wating状态,子线程最初进入的状态就是conn_new_cmd状态,这个状态主要是做一些清理。

[cpp]  view plain  copy
 
  1. case conn_new_cmd:  
  2.         --nreqs;//全局变量,记录每个libevent实例处理的事件,通过初始启动参数配置  
  3.         if (nreqs >= 0)//还可以处理请求  
  4.         {  
  5.             reset_cmd_handler(c);//整理缓冲区  
  6.         }  
  7.         else//拒绝请求  
  8.         {  
  9.             pthread_mutex_lock(&c->thread->stats.mutex);  
  10.             c->thread->stats.conn_yields++;//更新统计数据  
  11.             pthread_mutex_unlock(&c->thread->stats.mutex);  
  12.             if (c->rbytes > 0)//如果缓冲区有数据,则需要处理  
  13.             {  
  14.                 if (!update_event(c, EV_WRITE | EV_PERSIST))//更新libevent状态  
  15.                 {  
  16.                     if (settings.verbose > 0)  
  17.                         fprintf(stderr, "Couldn't update event\n");  
  18.                     conn_set_state(c, conn_closing);//关闭连接  
  19.                 }  
  20.             }  
  21.             stop = true;  
  22.         }  
  23.         break;  
  24. //整理缓冲区  
  25. static void reset_cmd_handler(conn *c)  
  26. {  
  27.     c->cmd = -1;  
  28.     c->substate = bin_no_state;  
  29.     if (c->item != NULL)//还有item  
  30.     {  
  31.         item_remove(c->item);//删除item,本篇不分析其实现,后续分析  
  32.         c->item = NULL;  
  33.     }  
  34.     conn_shrink(c);//整理缓冲区  
  35.     if (c->rbytes > 0)//缓冲区还有数据  
  36.     {  
  37.         conn_set_state(c, conn_parse_cmd);//更新状态  
  38.     }  
  39.     else//如果没有数据  
  40.     {  
  41.         conn_set_state(c, conn_waiting);//进入等待状态,状态机没有数据要处理,就进入这个状态  
  42.     }  
  43. }  
  44. //缩小缓冲区  
  45. static void conn_shrink(conn *c)  
  46. {  
  47.     assert(c != NULL);  
  48.   
  49.     if (IS_UDP(c->transport))//如果是UDP协议,不牵涉缓冲区管理  
  50.         return;  
  51.     //读缓冲区空间大小>READ_BUFFER_HIGHWAT && 已经读到的数据还没解析的数据小于 DATA_BUFFER_SIZE  
  52.     if (c->rsize > READ_BUFFER_HIGHWAT && c->rbytes < DATA_BUFFER_SIZE)  
  53.     {  
  54.         char *newbuf;  
  55.   
  56.         if (c->rcurr != c->rbuf)  
  57.             memmove(c->rbuf, c->rcurr, (size_t) c->rbytes);//目前数据是从rcurr开始的,移动数据到rbuf中  
  58.   
  59.         newbuf = (char *) realloc((void *) c->rbuf, DATA_BUFFER_SIZE);//按DATA_BUFFER_SIZE扩大缓冲区  
  60.   
  61.         if (newbuf)  
  62.         {  
  63.             c->rbuf = newbuf;//更新读缓冲区  
  64.             c->rsize = DATA_BUFFER_SIZE;//更新读缓冲区大小  
  65.         }  
  66.         c->rcurr = c->rbuf;  
  67.     }  
  68.   
  69.     if (c->isize > ITEM_LIST_HIGHWAT)//需要写出的item的个数,也就是要发送给客户端的item的个数  
  70.     {  
  71.         item **newbuf = (item**) realloc((void *) c->ilist,ITEM_LIST_INITIAL * sizeof(c->ilist[0]));//增大存放item的空间  
  72.         if (newbuf)  
  73.         {  
  74.             c->ilist = newbuf;//更新信息  
  75.             c->isize = ITEM_LIST_INITIAL;//更新信息  
  76.         }  
  77.     }  
  78.   
  79.     if (c->msgsize > MSG_LIST_HIGHWAT)//msghdr的个数,memcached发送消息是通过sendmsg批量发送的  
  80.     {  
  81.         struct msghdr *newbuf = (struct msghdr *) realloc((void *) c->msglist,MSG_LIST_INITIAL * sizeof(c->msglist[0]));//增大空间  
  82.         if (newbuf)  
  83.         {  
  84.             c->msglist = newbuf;//更新信息  
  85.             c->msgsize = MSG_LIST_INITIAL;//更新信息  
  86.         }  
  87.     }  
  88.   
  89.     if (c->iovsize > IOV_LIST_HIGHWAT)//msghdr里面iov的数量  
  90.     {  
  91.         struct iovec *newbuf = (struct iovec *) realloc((void *) c->iov,IOV_LIST_INITIAL * sizeof(c->iov[0]));//增大空间  
  92.         if (newbuf)  
  93.         {  
  94.             c->iov = newbuf;//更新信息  
  95.             c->iovsize = IOV_LIST_INITIAL;//更新信息  
  96.         }  
  97.     }  
  98. }  

从conn_new_cmd状态会进入conn_parse_cmd状态(如果有数据)或者conn_waiting(如果没有数据)状态,下面看看conn_waiting状态。

[cpp]  view plain  copy
 
  1. case conn_waiting:  
  2.         if (!update_event(c, EV_READ | EV_PERSIST))//修改libevent状态,读取数据  
  3.         {  
  4.             if (settings.verbose > 0)  
  5.                 fprintf(stderr, "Couldn't update event\n");  
  6.             conn_set_state(c, conn_closing);  
  7.             break;  
  8.         }  
  9.   
  10.         conn_set_state(c, conn_read);//进入读数据状态  
  11.         stop = true;  
  12.         break;  
  13. //更新libevent状态,也就是删除libevent事件后,重新注册libevent事件  
  14. static bool update_event(conn *c, const int new_flags)  
  15. {  
  16. assert(c != NULL);  
  17.   
  18. struct event_base *base = c->event.ev_base;  
  19. if (c->ev_flags == new_flags)  
  20.     return true;  
  21. if (event_del(&c->event) == -1)//删除旧的事件  
  22.     return false;  
  23. event_set(&c->event, c->sfd, new_flags, event_handler, (void *) c);//注册新事件  
  24. event_base_set(base, &c->event);  
  25. c->ev_flags = new_flags;  
  26. if (event_add(&c->event, 0) == -1)  
  27.     return false;  
  28. return true;  
  29. }  

备注:图片参考地址,http://blog.chinaunix.net/uid-27767798-id-3415510.html


Memcached源码分析之状态机(二)

通过前面一篇文章分析得知,conn_wating状态是在等待读取数据,conn_wating通过修改libevent事件(修改为读事件)之后就进入了conn_read状态,该状态就是从网络中读取数据,下面我们详细分析conn_read状态。

[cpp]  view plain  copy
 
  1. case conn_read:  
  2.         res = IS_UDP(c->transport) ? try_read_udp(c) : try_read_network(c);//判断采用UDP协议还是TCP协议  
  3.   
  4.         switch (res)  
  5.         {  
  6.         case READ_NO_DATA_RECEIVED://未读取到数据  
  7.             conn_set_state(c, conn_waiting);//继续等待  
  8.             break;  
  9.         case READ_DATA_RECEIVED://读取数据  
  10.             conn_set_state(c, conn_parse_cmd);//开始解析数据  
  11.             break;  
  12.         case READ_ERROR://读取发生错误  
  13.             conn_set_state(c, conn_closing);//关闭连接  
  14.             break;  
  15.         case READ_MEMORY_ERROR: //申请内存空间错误,继续尝试  
  16.             break;  
  17.         }  
  18.         break;  
  19. //采用TCP协议,从网络读取数据  
  20. static enum try_read_result try_read_network(conn *c)  
  21. {  
  22. enum try_read_result gotdata = READ_NO_DATA_RECEIVED;  
  23. int res;  
  24. int num_allocs = 0;  
  25. assert(c != NULL);  
  26. //rcurr标记读缓冲区的开始位置,如果不在,通过memmove调整  
  27. if (c->rcurr != c->rbuf)  
  28. {  
  29.     if (c->rbytes != 0)  
  30.         memmove(c->rbuf, c->rcurr, c->rbytes);  
  31.     c->rcurr = c->rbuf;//rcurr指向读缓冲区起始位置  
  32. }  
  33.   
  34. while (1)//循环读取  
  35. {  
  36.     if (c->rbytes >= c->rsize)//已经读取到的数据大于读缓冲区的大小  
  37.     {  
  38.         if (num_allocs == 4)  
  39.         {  
  40.             return gotdata;  
  41.         }  
  42.         ++num_allocs;  
  43.         char *new_rbuf = realloc(c->rbuf, c->rsize * 2);//按2倍扩容空间  
  44.         if (!new_rbuf)//realloc发生错误,也就是申请内存失败  
  45.         {  
  46.             if (settings.verbose > 0)  
  47.                 fprintf(stderr, "Couldn't realloc input buffer\n");  
  48.             c->rbytes = 0; //忽略已经读取到的数据  
  49.             out_string(c, "SERVER_ERROR out of memory reading request");  
  50.             c->write_and_go = conn_closing;//下一个状态就是conn_closing状态  
  51.             return READ_MEMORY_ERROR;//返回错误  
  52.         }  
  53.         c->rcurr = c->rbuf = new_rbuf;//读缓冲区指向新的缓冲区  
  54.         c->rsize *= 2;//读缓冲区的大小扩大2倍  
  55.     }  
  56.   
  57.     int avail = c->rsize - c->rbytes;//读缓冲区剩余空间  
  58.     res = read(c->sfd, c->rbuf + c->rbytes, avail);//执行网络读取,这个是非阻塞的读  
  59.     if (res > 0)//如果读取到了数据  
  60.     {  
  61.         pthread_mutex_lock(&c->thread->stats.mutex);  
  62.         c->thread->stats.bytes_read += res;//更新线程的统计数据  
  63.         pthread_mutex_unlock(&c->thread->stats.mutex);  
  64.         gotdata = READ_DATA_RECEIVED;//返回读取到数据的状态  
  65.         c->rbytes += res;//读取到的数据个数增加res  
  66.         if (res == avail)//最多读取到avail个,如果已经读到了,则可以尝试继续读取  
  67.         {  
  68.             continue;  
  69.         }  
  70.         else//否则,小于avail,表示已经没数据了,退出循环。  
  71.         {  
  72.             break;  
  73.         }  
  74.     }  
  75.     if (res == 0)//表示已经断开网络连接了  
  76.     {  
  77.         return READ_ERROR;  
  78.     }  
  79.     if (res == -1)//因为是非阻塞的,所以会返回下面的两个错误码  
  80.     {  
  81.         if (errno == EAGAIN || errno == EWOULDBLOCK)  
  82.         {  
  83.             break;  
  84.         }  
  85.         return READ_ERROR;//如果返回为负数,且不是上面两个数,则表示发生了其他错误,返回READ_ERROR  
  86.     }  
  87. }  
  88. return gotdata;  
  89. }  

上面描述的是TCP的数据读取,下面我们分析下UDP的数据读取,UDP是数据报的形式,读取到一个,就是一个完整的数据报,所以其处理过程简单。

[cpp]  view plain  copy
 
  1. //UDP读取网络数据  
  2. static enum try_read_result try_read_udp(conn *c)  
  3. {  
  4. int res;  
  5.   
  6. assert(c != NULL);  
  7.   
  8. c->request_addr_size = sizeof(c->request_addr);  
  9. res = recvfrom(c->sfd, c->rbuf, c->rsize, 0, &c->request_addr,  
  10.         &c->request_addr_size);//执行UDP的网络读取  
  11. if (res > 8)//UDP数据包大小大于8,已经有可能是业务数据包  
  12. {  
  13.     unsigned char *buf = (unsigned char *) c->rbuf;  
  14.     pthread_mutex_lock(&c->thread->stats.mutex);  
  15.     c->thread->stats.bytes_read += res;//更新每个线程的统计数据  
  16.     pthread_mutex_unlock(&c->thread->stats.mutex);  
  17.   
  18.     /* Beginning of UDP packet is the request ID; save it. */  
  19.     c->request_id = buf[0] * 256 + buf[1];//UDP为了防止丢包,增加了确认字段  
  20.   
  21.     /* If this is a multi-packet request, drop it. */  
  22.     if (buf[4] != 0 || buf[5] != 1)//一些业务的特征信息判断  
  23.     {  
  24.         out_string(c, "SERVER_ERROR multi-packet request not supported");  
  25.         return READ_NO_DATA_RECEIVED;  
  26.     }  
  27.   
  28.     /* Don't care about any of the rest of the header. */  
  29.     res -= 8;  
  30.     memmove(c->rbuf, c->rbuf + 8, res);//调整缓冲区  
  31.   
  32.     c->rbytes = res;//更新信息  
  33.     c->rcurr = c->rbuf;  
  34.     return READ_DATA_RECEIVED;  
  35. }  
  36. return READ_NO_DATA_RECEIVED;  
  37. }  

Memcached源码分析之状态机(三)

按前面2篇文章的分析可以知道,从网络读取了数据之后,将会进入conn_parse_cmd状态,该状态是按协议来解析读取到的网络数据。

[cpp]  view plain  copy
 
  1. case conn_parse_cmd:  
  2.                 //解析数据  
  3.                 if (try_read_command(c) == 0)  
  4.         {  
  5.                      //如果读取到的数据不够,我们继续等待,等读取到的数据够了,再进行解析  
  6.                      conn_set_state(c, conn_waiting);  
  7.         }  
  8.   
  9.         break;  
  10. //memcached支持二进制协议和文本协议  
  11. static int try_read_command(conn *c)  
  12. {  
  13. assert(c != NULL);  
  14. assert(c->rcurr <= (c->rbuf + c->rsize));  
  15. assert(c->rbytes > 0);  
  16.   
  17. if (c->protocol == negotiating_prot || c->transport == udp_transport)  
  18. {  
  19.     //二进制协议有标志,按标志进行区分  
  20.     if ((unsigned char) c->rbuf[0] == (unsigned char) PROTOCOL_BINARY_REQ)  
  21.     {  
  22.         c->protocol = binary_prot;//二进制协议  
  23.     }  
  24.     else  
  25.     {  
  26.         c->protocol = ascii_prot;//文本协议  
  27.     }  
  28.   
  29.     if (settings.verbose > 1)  
  30.     {  
  31.         fprintf(stderr, "%d: Client using the %s protocol\n", c->sfd,  
  32.                 prot_text(c->protocol));  
  33.     }  
  34. }  
  35. //如果是二进制协议  
  36. if (c->protocol == binary_prot)  
  37. {  
  38.     //二进制协议读取到的数据小于二进制协议的头部长度  
  39.     if (c->rbytes < sizeof(c->binary_header))  
  40.     {  
  41.         //返回继续读数据  
  42.         return 0;  
  43.     }  
  44.     else  
  45.     {  
  46. #ifdef NEED_ALIGN  
  47.         //如果需要对齐,则按8字节对齐,对齐能提高CPU读取的效率  
  48.         if (((long)(c->rcurr)) % 8 != 0)  
  49.         {  
  50.             //调整缓冲区  
  51.             memmove(c->rbuf, c->rcurr, c->rbytes);  
  52.             c->rcurr = c->rbuf;  
  53.             if (settings.verbose > 1)  
  54.             {  
  55.                 fprintf(stderr, "%d: Realign input buffer\n", c->sfd);  
  56.             }  
  57.         }  
  58. #endif  
  59.         protocol_binary_request_header* req;//二进制协议头  
  60.         req = (protocol_binary_request_header*) c->rcurr;  
  61.         //调试信息  
  62.         if (settings.verbose > 1)  
  63.         {  
  64.             /* Dump the packet before we convert it to host order */  
  65.             int ii;  
  66.             fprintf(stderr, "<%d Read binary protocol data:", c->sfd);  
  67.             for (ii = 0; ii < sizeof(req->bytes); ++ii)  
  68.             {  
  69.                 if (ii % 4 == 0)  
  70.                 {  
  71.                     fprintf(stderr, "\n<%d   ", c->sfd);  
  72.                 }  
  73.                 fprintf(stderr, " 0x%02x", req->bytes[ii]);  
  74.             }  
  75.             fprintf(stderr, "\n");  
  76.         }  
  77.   
  78.         c->binary_header = *req;  
  79.         c->binary_header.request.keylen = ntohs(req->request.keylen);//二进制协议相关内容  
  80.         c->binary_header.request.bodylen = ntohl(req->request.bodylen);  
  81.         c->binary_header.request.cas = ntohll(req->request.cas);  
  82.         //判断魔数是否合法,魔数用来防止TCP粘包  
  83.         if (c->binary_header.request.magic != PROTOCOL_BINARY_REQ)  
  84.         {  
  85.             if (settings.verbose)  
  86.             {  
  87.                 fprintf(stderr, "Invalid magic:  %x\n",  
  88.                         c->binary_header.request.magic);  
  89.             }  
  90.             conn_set_state(c, conn_closing);  
  91.             return -1;  
  92.         }  
  93.   
  94.         c->msgcurr = 0;  
  95.         c->msgused = 0;  
  96.         c->iovused = 0;  
  97.         if (add_msghdr(c) != 0)  
  98.         {  
  99.             out_string(c, "SERVER_ERROR out of memory");  
  100.             return 0;  
  101.         }  
  102.   
  103.         c->cmd = c->binary_header.request.opcode;  
  104.         c->keylen = c->binary_header.request.keylen;  
  105.         c->opaque = c->binary_header.request.opaque;  
  106.         //清除客户端传递的cas值  
  107.         c->cas = 0;  
  108.   
  109.         dispatch_bin_command(c);//协议数据处理  
  110.   
  111.         c->rbytes -= sizeof(c->binary_header);//更新已经读取到的字节数据  
  112.         c->rcurr += sizeof(c->binary_header);//更新缓冲区的路标信息  
  113.     }  
  114. }  

文本协议的过程和二进制协议的过程类似,此处不分析,另外dispatch_bin_command是处理具体的(比如get,set等)操作的,和是二进制协议具体相关的,解析完一些数据之后,会进入到conn_nread的流程,也就是读取指定数目数据的过程,这个过程主要是做具体的操作了,比如get,add,set操作。

[cpp]  view plain  copy
 
  1. case bin_read_set_value:  
  2.         complete_update_bin(c);//执行Update操作  
  3.         break;  
  4. case bin_reading_get_key:  
  5.         process_bin_get(c);//执行get操作  
  6.         break;  
状态机的整个处理过程就介绍到这里,其他的状态我们就不介绍了,后续的文章主要是分析一些数据的操作和内存结构,了解了这些之后,其实其他状态就相对容易很多。


上文来自:http://blog.csdn.net/lcli2009?viewmode=contents

你可能感兴趣的:(C++,源码,memcached,C++11)