• memcached(五)--源码分析,启动


      源码memcached.c中,main入口函数。

    第一步,根据memcached的启动参数做校验参数、配置

    main函数中,几乎600行代码都是这些参数校验。吐槽一个。

    第二步,初始化

      2.1:初始化主线程的libevent。

    main_base = event_init();

      2.2:初始化memcached的stats信息。

      在文本协议的memcached中,我们nc/telent后输入stats命令,会很快地输出一些当前memcached的信息的。这些就是stats信息。并不是输入stats的时候才遍历统计出来的。而是已经保存好了这份信息。代码调用在main函数中的:

    stats_init();

      具体的统计信息,可以在memcached.h这个文件中找到:

    /**
     * Global stats.
     */
    struct stats {
        pthread_mutex_t mutex;
        unsigned int  curr_items;
        unsigned int  total_items;
        uint64_t      curr_bytes;
        unsigned int  curr_conns;
        unsigned int  total_conns;
        uint64_t      rejected_conns;
        uint64_t      malloc_fails;
        unsigned int  reserved_fds;
        unsigned int  conn_structs;
        uint64_t      get_cmds;
        uint64_t      set_cmds;
        uint64_t      touch_cmds;
        uint64_t      get_hits;
        uint64_t      get_misses;
        uint64_t      touch_hits;
        uint64_t      touch_misses;
        uint64_t      evictions;
        uint64_t      reclaimed;
        time_t        started;          /* when the process was started */
        bool          accepting_conns;  /* whether we are currently accepting */
        uint64_t      listen_disabled_num;
        unsigned int  hash_power_level; /* Better hope it's not over 9000 */
        uint64_t      hash_bytes;       /* size used for hash tables */
        bool          hash_is_expanding; /* If the hash table is being expanded */
        uint64_t      expired_unfetched; /* items reclaimed but never touched */
        uint64_t      evicted_unfetched; /* items evicted but never touched */
        bool          slab_reassign_running; /* slab reassign in progress */
        uint64_t      slabs_moved;       /* times slabs were moved around */
        uint64_t      lru_crawler_starts; /* Number of item crawlers kicked off */
        bool          lru_crawler_running; /* crawl in progress */
        uint64_t      lru_maintainer_juggles; /* number of LRU bg pokes */
    };

      2.3:hash桶初始化

      代码main函数中的:

    assoc_init(settings.hashpower_init);

      在memcached中,保存着一份hash表用来存放memcached key。默认这个hash表是2^16(65536)个key。后续会根据规则动态扩容这个hash表的。如果希望启动的时候,这个hash表更大,可以-o 参数调节。

      hash表中, memcached key作为key,value是item指针,并不是item value。

      2.4:初始化connection。

    conn_init()

       也就是 memcached启动参数中的-c参数,默认1024。

      为了更快地找到connection的fd(文件描述符),实际上申请的connection会比配置的更大一点。

    /*
     * Initializes the connections array. We don't actually allocate connection
     * structures until they're needed, so as to avoid wasting memory when the
     * maximum connection count is much higher than the actual number of
     * connections.
     *
     * This does end up wasting a few pointers' worth of memory for FDs that are
     * used for things other than connections, but that's worth it in exchange for
     * being able to directly index the conns array by FD.
     */
    static void conn_init(void) {
        /* We're unlikely to see an FD much higher than maxconns. */
        int next_fd = dup(1);
        int headroom = 10;      /* account for extra unexpected open FDs */
        struct rlimit rl;
    
        max_fds = settings.maxconns + headroom + next_fd;
    
        /* But if possible, get the actual highest FD we can possibly ever see. */
        if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
            max_fds = rl.rlim_max;
        } else {
            fprintf(stderr, "Failed to query maximum file descriptor; "
                            "falling back to maxconns
    ");
        }
    
        close(next_fd);
    
        if ((conns = calloc(max_fds, sizeof(conn *))) == NULL) {
            fprintf(stderr, "Failed to allocate connection structures
    ");
            /* This is unrecoverable so bail out early. */
            exit(1);
        }
    }

      2.5:初始化slabs。

      在2.3的hash桶中初始化的是key。slabs初始化的是这些key对应的value。下面摘录关键代码:

        while (++i < MAX_NUMBER_OF_SLAB_CLASSES-1 && size <= settings.item_size_max / factor) {
            /* Make sure items are always n-byte aligned */
            if (size % CHUNK_ALIGN_BYTES)
                size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
    
            slabclass[i].size = size;
            slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
            size *= factor;
            if (settings.verbose > 1) {
                fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u
    ",
                        i, slabclass[i].size, slabclass[i].perslab);
            }
        }

      在初始化slab的时候,下一个slab的size(chunk size)总是大于等于当前slab的size的

      2.6:初始化worker线程。

    memcached_thread_init(settings.num_threads, main_base);

      worker线程和main线程,组成了libevent的reactor模式。

      2.7:定时器

    clock_handler(0, 0, 0);

      用于对比对象是否过期。

    第三步、libevent主线程监听事件

        /* enter the event loop */
        if (event_base_loop(main_base, 0) != 0) {
            retval = EXIT_FAILURE;
        }

      主线程启动堆栈:

    server_sockets——>
    server_socket——>
    conn_new——>
    event_handler——>
    drive_machine——>
    try_read_command(这里会判定,是文本协议还是二进制协议)

    第四步、关闭hash桶线程

      在2.3的初始化步骤中,有线程操作。这里明确关闭这个线程。

    void stop_assoc_maintenance_thread() {
        mutex_lock(&maintenance_lock);
        do_run_maintenance_thread = 0;
        pthread_cond_signal(&maintenance_cond);
        mutex_unlock(&maintenance_lock);
    
        /* Wait for the maintenance thread to stop */
        pthread_join(maintenance_tid, NULL);
    }

      memcached启动的主要流程就是这些了。

      最后来个图片,描述一下启动后的memcached结构。

      源码github上有,见:https://github.com/memcached/memcached/blob/master/memcached.c  文件有点大,可能浏览器卡顿一下。这里肯定就不贴出来了^_^ 

  • 相关阅读:
    谷粒商城所学知识点整理总结
    谷粒商城项目介绍
    JVM 中的垃圾回收
    对象的创建和分配
    JVM 中的异常
    JVM 中的StringTable
    一个 java 文件的执行过程详解
    复制表的方法
    从 Vue parseHTML 来学习正则表达式
    Visual Studio 2022 预览版下载来了(x64位)
  • 原文地址:https://www.cnblogs.com/ELMND/p/4581729.html
Copyright © 2020-2023  润新知