memcached的执行流程

来源:互联网 发布:appium python ios 编辑:程序博客网 时间:2024/04/28 20:28

1、libevent的使用方法

初始化   struct event_base main_base = event_init();  //多孔插座

事件设置 event_set(&ev, listen_fd, EV_READ | EV_PERSIST, cb, *arg) //设置灯泡

                  event_base_set(main_base, &ev);//设置灯泡属于哪个插座

事件添加 event_add(&ev, NULL)  //插上电板

进入事件循环 event_base_loop(main_base, 0); //进入事件循环

 

struct event {  TAILQ_ENTRY (event) ev_next;  TAILQ_ENTRY (event) ev_active_next;  TAILQ_ENTRY (event) ev_signal_next;  unsigned int min_heap_idx; /* for managing timeouts */  struct event_base *ev_base;  int ev_fd;  short ev_events;  short ev_ncalls;  short *ev_pncalls; /* Allows deletes in callback */  struct timeval ev_timeout;  int ev_pri;  /* smaller numbers are higher priority */  void (*ev_callback)(int, short, void *arg);  void *ev_arg;  int ev_res;  /* result passed to event callback */  int ev_flags;  };    

 

ev_events:event关注的事件类型,它可以是以下3种类型:I/O事件: EV_WRITE和EV_READ;定时事件:EV_TIMEOUT;信号: EV_SIGNAL;辅助选项:EV_PERSIST,表明是一个永久事件。

#define EV_TIMEOUT 0x01   #define EV_READ  0x02   #define EV_WRITE 0x04   #define EV_SIGNAL 0x08   #define EV_PERSIST 0x10 /* Persistant event */

2、主程序详细流程

(1)初始化阶段

settings_init()初始化settings结构体。

static void settings_init(void) {    settings.use_cas = true;    settings.access = 0700;    settings.port = 11211;    settings.udpport = 11211;    /* By default this string should be NULL for getaddrinfo() */    settings.inter = NULL;    settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */    settings.maxconns = 1024;         /* to limit connections-related memory to about 5MB */    settings.verbose = 0;    settings.oldest_live = 0;    settings.evict_to_free = 1;       /* push old items out of cache when memory runs out */    settings.socketpath = NULL;       /* by default, not using a unix socket */    settings.factor = 1.25;    settings.chunk_size = 48;         /* space for a modest key and value */    settings.num_threads = 4;         /* N workers */    settings.num_threads_per_udp = 0;    settings.prefix_delimiter = ':';    settings.detail_enabled = 0;    settings.reqs_per_event = 20;    settings.backlog = 1024;    settings.binding_protocol = negotiating_prot;    settings.item_size_max = 1024 * 1024; /* The famous 1MB upper limit. */    settings.maxconns_fast = false;    settings.hashpower_init = 0;    settings.slab_reassign = false;    settings.slab_automove = 0;}

setbuf(stderr, NULL);设置stderr为无缓冲。

int getopt(int argc, char* const argv[ ], const char* optstring);获取参数用于设置settings结构体。

查看和设置资源限制(getrlimit(), setrlimit()),放弃root权利等。

event_init();

stats_init();初始化struct stats(global stats)。

assoc_init(settings.hashpower_init);->初始化primary_hashtable(Main hash table, 类型为static item**)。

conn_init();->初始化freeconns(Free list management for connections, 类型为static conn)为全0,初始化freecurr为0。

slabs_init(settings.maxbytes, settings.factor, preallocate);初始化slab class,详见数据结构一节。

 

(2)线程创建阶段

thread_init(settings.num_threads, main_base);

        初始化锁和条件变量,尤其是item_locks[ ],初始化static LIBEVENT_THREAD *threads和static LIBEVENT_DISPATCHER_THREAD dispatcher_thread(即为主线程),之后调用setup_thread(&threads[i])和create_worker()。其中setup_thread()设置事件处理函数thread_libevent_process,但尚未进入事件循环,也还没有创建线程;之后初始化new_conn_queue成员(queue of new connections to handle);之后调用cache_create()初始化suffix_cache成员,创建了一个cache,name为“suffix”。create_worker(worker_libevent, &threads[i])创建4个线程,线程函数为worker_libevent()。worker_libevent()只是简单的进入事件循环

        注意:利用条件变量使得,thread_init()会阻塞,直到所有线程都已创建完毕。

        关于其中的pthread_key_create()和pthread_setspecific(),可见http://blog.csdn.net/yangzhiloveyou/article/details/8043573.

/* * Initializes the thread subsystem, creating various worker threads. * * nthreads  Number of worker event handler threads to spawn * main_base Event base for main thread */void thread_init(int nthreads, struct event_base *main_base) {    int         i;    int         power;    pthread_mutex_init(&cache_lock, NULL);    pthread_mutex_init(&stats_lock, NULL);    pthread_mutex_init(&init_lock, NULL);    pthread_cond_init(&init_cond, NULL);    pthread_mutex_init(&cqi_freelist_lock, NULL);    cqi_freelist = NULL;    /* Want a wide lock table, but don't waste memory */    if (nthreads < 3) {        power = 10;    } else if (nthreads < 4) {        power = 11;    } else if (nthreads < 5) {        power = 12;    } else {        /* 8192 buckets, and central locks don't scale much past 5 threads */        power = 13;    }    item_lock_count = hashsize(power);    item_locks = calloc(item_lock_count, sizeof(pthread_mutex_t));    if (! item_locks) {        perror("Can't allocate item locks");        exit(1);    }    for (i = 0; i < item_lock_count; i++) {        pthread_mutex_init(&item_locks[i], NULL);    }    pthread_key_create(&item_lock_type_key, NULL);    //用于在线程间传递当前锁类型,全局锁还是细粒度锁    pthread_mutex_init(&item_global_lock, NULL);    threads = calloc(nthreads, sizeof(LIBEVENT_THREAD));    if (! threads) {        perror("Can't allocate thread descriptors");        exit(1);    }    dispatcher_thread.base = main_base;    dispatcher_thread.thread_id = pthread_self();    for (i = 0; i < nthreads; i++) {        int fds[2];        if (pipe(fds)) {            perror("Can't create notify pipe");            exit(1);        }        threads[i].notify_receive_fd = fds[0];        threads[i].notify_send_fd = fds[1];        setup_thread(&threads[i]);        /* Reserve three fds for the libevent base, and two for the pipe */        stats.reserved_fds += 5;    }    /* Create threads after we've done all the libevent setup. */    for (i = 0; i < nthreads; i++) {        create_worker(worker_libevent, &threads[i]);    }    /* Wait for all the threads to set themselves up before returning. */    pthread_mutex_lock(&init_lock);    wait_for_thread_registration(nthreads);    pthread_mutex_unlock(&init_lock);}


start_assoc_maintenance_thread() 创建线程maintenance_tid,线程函数为assoc_maintenance_thread()。

        assoc_maintenance_thread()在expanding(如何expanding暂不讨论)之后,调用switch_item_lock_type(ITEM_LOCK_GRANULAR),使得所有线程开始使用细粒度锁(通过管道通知各线程,并阻塞等待各线程设置完毕);之后调用slabs_rebalancer_resume()解锁slabs_rebalance_lock锁(什么时候上的锁?什么作用?);最后调用pthread_cond_wait(&maintenance_cond, &cache_lock);阻塞,等待其他线程使用pthread_cond_signal()唤醒(注意pthread_cond_signal()不能在pthread_cond_wait()之前执行,否则唤醒信号丢失)。

如果settings.slab_reassign为true,调用start_slab_maintenance_thread()创建相关线程。此处为false,所以不调用,暂不讨论。

clock_handler(0, 0, 0);调用evtimer_add()添加计数器clockevent,定时一秒,处理函数为clock_handler(),即自身。可以看出clock_handler()的作用就是每一秒钟更新一次current_time。

 

(3)socket创建阶段

如果settings.socketpath != NULL,就调用server_socket_unix()创建Unix套接字;此处为NULL,不执行,暂不讨论。

server_sockets(settings.port, tcp_transport, portnumber_file)调用server_socket()。server_socket()中调用new_socket()创建socket,并设置为非阻塞模式;设置SO_REUSEADDR;对于TCP,设置SO_KEEPALIVE,SO_LINGER,TCP_NODELAY;对于TCP,调用conn_new()(对于UDP,调用dispatch_conn_new())。
        conn_new()先调用conn_from_freelist()从freelist获得一个connection(此时freelist为空,所以返回NULL);初始化一个struct conn;设置listen套接字sfd的事件处理函数为event_handler();如果事件添加失败,则将已初始化好的conn加入freelist(conn_add_to_freelist())。

        此处server_sockets()创建了两个监听端口,一个IPV4,一个IPV6,两个struct conn最终链入static conn* listen_conn链表

        注意:在<netdb.h>中,#define NI_MAXSERV 32; #define NI_MAXHOST 1025。

        SO_REUSEADDR:允许重启的监听服务器bind其众所周知端口,即使以前建立的将该端口用作它们本地端口的连接仍存在。允许进程绑定一个处于TIME_WAIT的端口。

        SO_KEEPALIVE:如果两小时无数据交换,发送存活探测分节。

        SO_LINGER:设置close之后对缓冲区和套接口的处理;此处设置l_onff为0,即在套接口上不能再发出发送和接收请求,套接口发送缓冲区中内容被发送到对端。

        TCP_NODELAY:将数据包直接发送,而不是组成大的分组再发送。

 server_sockets(settings.udpport, udp_transport, portnumber_file)针对UDP,同样调用server_socket()。server_socket()中创建socket并调用maximize_sndbuf()设置发送缓存为最大值。之后bind()。最后调用dispatch_conn_new()。

         dispatch_conn_new()首先调用cqi_new()从cqi_freelist获得一个CQ_ITEM(struct conn_queue_item,An item in the connection queue),初始化CQ_ITEM;分配一个thread,调用cq_push(thread->new_conn_queue, item)将该item链入thread的conn_queue中;最后写一个‘c’到thread,这将使thread调用事件处理函数thread_libevent_process(),进一步调用conn_new(item->sfd, item->init_state, item->event_flags, item->read_buffer_size, item->transport, me->base))。可以看出,实际上主进程将item传送给thread,thread通过这个item来创建一个conn

/* * Dispatches a new connection to another thread. This is only ever called * from the main thread, either during initialization (for UDP) or because * of an incoming connection. */void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags,                       int read_buffer_size, enum network_transport transport) {    CQ_ITEM *item = cqi_new();    int tid = (last_thread + 1) % settings.num_threads;    //以此种方式来取出线程    LIBEVENT_THREAD *thread = threads + tid;    last_thread = tid;    item->sfd = sfd;    item->init_state = init_state;    item->event_flags = event_flags;    item->read_buffer_size = read_buffer_size;    item->transport = transport;    //将新item放至threads的new_conn_queue队列中    cq_push(thread->new_conn_queue, item);    MEMCACHED_CONN_DISPATCH(sfd, thread->thread_id);    //写一个字节启动新的线程    if (write(thread->notify_send_fd, "", 1) != 1) {        perror("Writing to thread notify pipe");    }}

        至此,创建了两个tcp socket(分别用于ipv4和ipv6),主进程创建conn并监听;主进程创建了两个udp socket(分别用于ipv4和ipv6),4个子进程创建conn并各自管理。如下:

<28 server listening (auto-negotiate)<29 server listening (auto-negotiate)<30 send buffer was 112640, now 268435456<30 server listening (udp)<30 server listening (udp)<30 server listening (udp)<30 server listening (udp)<31 send buffer was 112640, now 268435456<31 server listening (udp)<31 server listening (udp)<31 server listening (udp)<31 server listening (udp)


(5)最后阶段

event_base_loop(main_base, 0)主进程进入事件循环。

        小结一下:主线程中的事件主要是针对两个tcp socket的,事件处理函数为event_handler();还有一个计数器事件clockevent,定时一秒,处理函数为clock_handler()。四个工作线程的事件一样,都是一个管道notify_receive_fd(事件处理函数为thread_libevent_process()),一个udp ipv4 socket,一个udp ipv6 socket,这两个的事件处理函数为event_handler() 。注意四个工作线程之前就进入事件循环了。

至此,程序就完全跑起来了。

 

(6)清理阶段

 默认情况下,event_base_loop()会一直循环,等待事件触发,然后调用他们的回调函数。直到没有添加的事件,或者调用了event_base_loopbreak()或者event_base_loopexit()。如果event_base_loop()退出,主进程将会执行一些清理过程,如下:

stop_assoc_maintenance_thread()简单的设置do_run_maintenance_thread = 0;线程maintenance_tid检测到do_run_maintenance_thread = 0就会自动退出。......

 

3、当client发起连接

执行命令telnet localhost 11211,向memcached发起tcp连接。memcached调用event_handler()处理。

        event_handler(const int fd, const short which, void *arg)其中which传入event.ev_events,为事件类型。event_handler()调用drive_machine(conn *c)。drive_machine()之后介绍。

原创粉丝点击