twemproxy源码解析1 源码文件总体简介

版权声明:本文为博主原创文章,未经博主允许不得转载! https://blog.csdn.net/qq_25560423/article/details/88852506

在正式开始阅读代码之前,先来看一下代码整体结构

代码规模不大只有1w多行,而且功能划分的比较清晰,包括:

  • 事件处理: event/nc_epoll.c、event/nc_event.h、event/nc_evport.c、event/nc_kqueue.c
  • 各种Hash函数: hashkit/nc_crc16.c、hashkit/nc_crc32.c、hashkit/nc_fnv.c、hashkit /nc_hashkit.h、hashkit/nc_hsieh.c、hashkit/nc_jenkins.c、hashkit /nc_ketama.c、hashkit/nc_md5.c、hashkit/nc_modula.c、hashkit/nc_murmur.c、 -hashkit/nc_one_at_a_time.c、hashkit/nc_random.c
  • 协议: proto/nc_memcache.c、proto/nc_proto.h、proto/nc_redis.c
  • 自定义的数据类型: nc_array.c、nc_array.h、nc_string.c、nc_string.h
  • 网络通信相关: nc_connection.c、nc_connection.h、nc_client.c、nc_client.h、nc_proxy.c、nc_proxy.h
  • 信号处理: nc_signal.c、nc_signal.h
  • 关键数据结构和算法: nc_rbtree.h、nc_rbtree.c、nc_queue.h、nc_request.c、nc_response.c、nc_mbuf.c、 nc_mbuf.h、- nc_message.c、nc_message.h、nc_server.c、nc_server.h
  • 统计、日志和工具: nc_stats.c、nc_stats.h、nc_log.c、nc_log.h、nc_util.c、nc_util.h
  • 配置文件: nc_conf.c、nc_conf.h
  • 主程序: nc.c、nc_core.c、nc_core.h

src/nc.c文件 是程序入口main函数所在,下面我给各个函数加了注释

int
main(int argc, char **argv)
{
    rstatus_t status; //rstatus_t 返回类型
    struct instance nci;//nc实例

    nc_set_default_options(&nci); //为nc实例设置一些默认的参数属性值 之后具体介绍
	
    status = nc_get_options(argc, argv, &nci); //获取参数 解析参数
    if (status != NC_OK) {
        nc_show_usage();
        exit(1);
    }

    if (show_version) { //打印版本号
        log_stderr("This is nutcracker-%s" CRLF, NC_VERSION_STRING);
        if (show_help) {
            nc_show_usage(); //这个函数就是打印twemproxy的所有参数介绍
        }

        if (describe_stats) {
            stats_describe();
        }

        exit(0);
    }

    if (test_conf) {
        if (!nc_test_conf(&nci)) {
            exit(1);
        }
        exit(0);
    }

    status = nc_pre_run(&nci);
    if (status != NC_OK) {
        nc_post_run(&nci);
        exit(1);
    }

    nc_run(&nci);

    nc_post_run(&nci);

    exit(1);
}

main函数的整理流程非常清晰,包含以下几步:
初始化option,设置默认值
从argv中获取option,若argv有错误则调用nc_show_usage()打印使用帮助
根据argv,判断是否是:show_version、show_help、describe_stats或者test_conf,是则执行相应逻辑后退出
依次调用nc_pre_run()、nc_run()和nc_post_run()
前面的步骤目前无关紧要,可以看出主要逻辑在nc_pre_run()、nc_run()和nc_post_run()函数中。这三个函数的定义也在nc.c当中。

下面是对main函数进一步介绍:

二、Twemproxy启动启动流程分析

对于一个由C/C++编写的应用程序而言,程序的启动点一般都是main函数,所以对一个程序进行分析的话,也一般都是有main函数作为入口,我们这里也不例外,以main函数作为入口。

找到文件nc.c定位到main,我们可以看到定义了一个变量,

struct instance nci;

下边我们看下这个instance的定义:

 struct instance {
        struct context  *ctx;                        /* active context */
        int             log_level;                   /* log level */
        char            *log_filename;               /* log filename */
        char            *conf_filename;              /* configuration filename */
        uint16_t        stats_port;                  /* stats monitoring port */
        int             stats_interval;              /* stats aggregation interval */
        char            *stats_addr;                 /* stats monitoring addr */
        char            hostname[NC_MAXHOSTNAMELEN]; /* hostname */
        size_t          mbuf_chunk_size;             /* mbuf chunk size */
        pid_t           pid;                         /* process id */
        char            *pid_filename;               /* pid filename */
        unsigned        pidfile:1;                   /* pid file created? */
    };

这个instance就相当于是一个twemproxy实例,后边整个程序的初始化很多都会用到。接下来就调用了nc_set_default_options()和nc_get_options()两个函数,其中nc_get_options这个函数是读取命令行参数。其中比较重要的几个参数分别有:

a) test_conf : 用于设置在启动twemproxy之前是否要对配置文件做检查,以确保配置文件格式的正确。

然后就会调用nc_pre_run,启动之前做一些预处理,包括:初始化日志级别以及日志文件;设置是否后台运行;对信号做初始化处理;是否创建pid文件。

接下来调用nc_run开始启动proxy;这个函数完成的工作就是调用core_start创建context,然后进入死循环调用core_loop开始整个事件循环的处理,接受请求并处理。当然,core_start以及core_loop这两个函数里边还包含了大量的处理工作,包括,配置文件解析以及读取,相关组件(server_pool,conf,context)的初始化等等,这些后边详细讲述。

下面介绍一下nc_pre_run、nc_run的逻辑:

nc_pre_run()的函数逻辑是:

  • 初始化日志
  • 启动守护进程
  • 初始化信号处理函数
  • 创建pidfile
  • 输出启动信息
  • 如果nc_pre_run()运行失败,main()函数的逻辑中会调用nc_post_run()回收资源

nc_post_run()函数的主要逻辑是:

  • 删除pidfile
  • 信号处理函数deinit(这里其实什么也不做)
  • 打印关闭信息
  • 日志deinit(关闭日志文件描述符)
    接下来就是比较重要的nc_run()

nc_run()函数主要逻辑是:

调用core_start(),初始化上下文ctx
进入死循环,调用core_loop()进入事件循环
检查退出条件status != NC_OK,退出循环
调用core_stop()结束
可以看出细节都封装到了 core_start()、core_loop()和core_stop() 当中,但是nc.c主程序的逻辑到这里就结束了。值得一提的是,nc_daemonize()函数封装了创建守护进程的一个标准逻辑。

总体来说,启动流程就是这些步骤,如下图:
1
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_25560423/article/details/88852506