【高效server实践】--memcached网络框架剥离实践

memcached通过epoll(使用libevent)实现异步服务,主要由"主线程"和多个"worker线程"构成,主线程负责监听网络链接,并且accept连接。当监听到连接accept成功后,把连接句柄FD传给其中的一个空闲work线程处理。空闲的worker线程接收到主线程传过来的连接句柄DF后,将其加入自己的epoll监听队列并处理该连接的读写事件。
   博主从memcahced剥离了基于线程池的收发包框架代码,linux下直接make编译可运行:https://github.com/donaldhuang/memcached_network
       用libevent做事件监听使用起来非常简单:
  event_flags=EV_READ | EV_PERSIST;//监听可读事件
  base=(event_base*)event_init();//初始化

   event_set(event, sfd, event_flags, base_event_handler,argvs);

//创建监听sfd句柄的可读事件,处理函数是base_event_handler,argvs是传入处理函数的参数

   event_base_set(base, event);//为创建的事件event指定事件基地
   event_add(event, time_tv);//把事件加入到事件基地进行监听,time_tv为epoll没有可读事件的超时时间
   event_base_loop(main_base,flag);//进入监听事件的循环,flag分为阻塞和非阻塞
   1)主线程根据用户的设置创建n个worker线程,每个线程有一个待处理的连接句柄队列conn_queue,且每个线程都使用libevent监听一个管道并设置了处理函数thread_libevent_process
    
threads=(LIBEVENT_THREAD*)calloc(nthreads,sizeof(LIBEVENT_THREAD));     //LIBEVENT_THREAD,加入libevent元素的thread结构 “数组”  
    if(!threads)
    {   
        perror("can't allocate thread des");
        exit(1);
    }       

    for(i=0;i

   2)主线程首先使用libevent监听一个主端口。当有连接事件到达时,accept获得一个连接句柄FD,把该FD封装到一个CQ_ITEM,然后选择一个空闲的Worker线程,将CQ_ITEM压入Worker线程的待处理的连接句柄队列中,并往Worker线程家庭的管道中写入一个"c"字符。
 CQ_ITEM *item=cqi_new();    
     int tid=(last_thread+1)%settings.num_threads;           //轮询选出workerThread(数组)
     LIBEVENT_THREAD *thread=workerThreads->threads+tid;
    last_thread=tid;
    item->sfd=sfd;                                          //封装必要的信息到item结构,后面会利用item封装为conn
     item->init_state=init_state;
    item->event_flags=event_flags;
    item->read_buffer_size=read_buffer_size;
    item->transport=transport;
 write(thread->notify_send_fd,"c",1);//主线程和workerThread的通信方式,写入到notify_send_fd告诉辅助线程item准备好了,可以处理
     last_thread++;

   3) Worker线程监听到了管道可读事件,触发thread_libevent_process处理函数。从待处理的连接句柄队列中取出一个CQ_ITEM,然后用libevent监听CQ_ITEM里面的连接句柄的可读事件,并设置事件处理函数event_handler
     
  LIBEVENT_THREAD *me=(LIBEVENT_THREAD*)arg;
       CQ_ITEM *item;
       char buf[1];
       if(read(fd,buf,1)!=1)
          fprintf(stderr,"can't read from libevent pipe\n");
     item=cq_pop(me->new_conn_queue);
    if(NULL!=item)
     {   
          conn *c= conn_new (item->sfd,item->init_state,item->event_flags,
                item->read_buffer_size,item->transport,me->base);

          if(NULL==c)
         {   
            if( IS_UDP(item->transport))
             {   
                  fprintf(stderr,"can't listen for events on UDP\n");
                 exit(1);
              }   
                else
               {   
                      fprintf(stderr,"can't listen for events on fd %d\n",item->sfd);
                 close(item->sfd);
              }   
         }   
        else
       {   
             c->thread=me;
        }
         cqi_free(item);
     }
conn *conn_new(const int sfd,enum conn_states init_state,const int event_flags,
               const int read_buffer_size,enum network_transport transport,
struct event_base *base)
{
c->sfd=sfd;
event_set(&c->event,sfd,event_flags,event_handler,(void*)c);
   event_base_set(base,&c->event);
        c->ev_flags=event_flags;

        if(event_add(&c->event,0)==-1)
        {   
        if( conn_add_to_freelist(c))
            conn_free(c);
        perror("event_add");
        return NULL;
        }   
     return c;
} 

你可能感兴趣的:(memcached,网络框架,异步epoll,高并发,线程池)