揭秘Nginx中如何使用红黑树优化数据操作

本文详细介绍了Nginx中红黑树的定义和使用,包括在ngx_cycle和ngx_open_file_cache模块中的具体应用。ngx_cycle中,红黑树用于配置文件的快照保存;ngx_open_file_cache中,红黑树用于管理文件缓存,提高文件访问效率。文章还展示了相关函数调用流程图,帮助理解其工作原理。
摘要由CSDN通过智能技术生成

一、Nginx定义的红黑树

Nginx定义的红黑树在/src/core/ngx_rbtree.h和/src/core/ngx_rbtree.c。定义了红黑树节点、红黑树、红黑树插入函数等等。
在这里插入图片描述

其中/src/core/ngx_rbtree.h的代码如下:


#ifndef _NGX_RBTREE_H_INCLUDED_
#define _NGX_RBTREE_H_INCLUDED_


#include <ngx_config.h>
#include <ngx_core.h>


typedef ngx_uint_t  ngx_rbtree_key_t;
typedef ngx_int_t   ngx_rbtree_key_int_t;


typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;

struct ngx_rbtree_node_s {
    ngx_rbtree_key_t       key;
    ngx_rbtree_node_t     *left;
    ngx_rbtree_node_t     *right;
    ngx_rbtree_node_t     *parent;
    u_char                 color;
    u_char                 data;
};


typedef struct ngx_rbtree_s  ngx_rbtree_t;

typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

struct ngx_rbtree_s {
    ngx_rbtree_node_t     *root;
    ngx_rbtree_node_t     *sentinel;
    ngx_rbtree_insert_pt   insert;
};


#define ngx_rbtree_init(tree, s, i)                                           \
    ngx_rbtree_sentinel_init(s);                                              \
    (tree)->root = s;                                                         \
    (tree)->sentinel = s;                                                     \
    (tree)->insert = i


void ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
void ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
void ngx_rbtree_insert_value(ngx_rbtree_node_t *root, ngx_rbtree_node_t *node,
    ngx_rbtree_node_t *sentinel);
void ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *root,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
ngx_rbtree_node_t *ngx_rbtree_next(ngx_rbtree_t *tree,
    ngx_rbtree_node_t *node);


#define ngx_rbt_red(node)               ((node)->color = 1)
#define ngx_rbt_black(node)             ((node)->color = 0)
#define ngx_rbt_is_red(node)            ((node)->color)
#define ngx_rbt_is_black(node)          (!ngx_rbt_is_red(node))
#define ngx_rbt_copy_color(n1, n2)      (n1->color = n2->color)


/* a sentinel must be black */

#define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)


static ngx_inline ngx_rbtree_node_t *
ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
    while (node->left != sentinel) {
        node = node->left;
    }

    return node;
}


#endif /* _NGX_RBTREE_H_INCLUDED_ */

二、Nginx使用红黑树的地方

Nginx使用红黑树的地方有:

  1. ngx_cycle。
  2. ngx_open_file_cache。
  3. ngx_resolver。
  4. ngx_event_openssl。
  5. ngx_event_timer。
  6. ngx_http_file_cache。
  7. ngx_http_geo_module。
  8. ngx_http_limit_conn_module。
  9. ngx_http_limit_req_module。
  10. ngx_stream_geo_module 。
  11. ngx_stream_limit_conn_module。

2.1、ngx_cycle

nginx的每个工作进程都有其自己的ngx_cycle。
在/src/core/ngx_cycle.h中定义的数据结构struct ngx_cycle_s引用了红黑树变量:

struct ngx_cycle_s{
	// ...
	ngx_rbtree_t              config_dump_rbtree;
	ngx_rbtree_node_t         config_dump_sentinel;
	// ...
}

(1)在nginx的main()函数中,会调用nginx_init_cycle函数初始化ngx_cycle_t。
/src/core/nginx.c

int ngx_cdecl
main(int argc, char *const *argv)
{
	// ...
	cycle = ngx_init_cycle(&init_cycle);
    if (cycle == NULL) {
        if (ngx_test_config) {
            ngx_log_stderr(0, "configuration file %s test failed",
                           init_cycle.conf_file.data);
        }

        return 1;
    }
	// ...
}

(2)在nginx_init_cycle函数初始化红黑树。提供红黑树插入函数ngx_str_rbtree_insert_value。
/src/core/ngx_cycle.c

ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{
// ...
	ngx_rbtree_init(&cycle->config_dump_rbtree, &cycle->config_dump_sentinel,
	                    ngx_str_rbtree_insert_value);
	                    
// ...
}

(3)在ngx_conf_add_dump函数轮询和插入红黑树节点。
/src/core/ngx_conf_file.c

static ngx_int_t
ngx_conf_add_dump(ngx_conf_t *cf, ngx_str_t *filename)
{
    off_t             size;
    u_char           *p;
    uint32_t          hash;
    ngx_buf_t        *buf;
    ngx_str_node_t   *sn;
    ngx_conf_dump_t  *cd;

    hash = ngx_crc32_long(filename->data, filename->len);

    sn = ngx_str_rbtree_lookup(&cf->cycle->config_dump_rbtree, filename, hash);

    if (sn) {
        cf->conf_file->dump = NULL;
        return NGX_OK;
    }

    p = ngx_pstrdup(cf->cycle->pool, filename);
    if (p == NULL) {
        return NGX_ERROR;
    }

    cd = ngx_array_push(&cf->cycle->config_dump);
    if (cd == NULL) {
        return NGX_ERROR;
    }

    size = ngx_file_size(&cf->conf_file->file.info);

    buf = ngx_create_temp_buf(cf->cycle->pool, (size_t) size);
    if (buf == NULL) {
        return NGX_ERROR;
    }

    cd->name.data = p;
    cd->name.len = filename->len;
    cd->buffer = buf;

    cf->conf_file->dump = buf;

    sn = ngx_palloc(cf->temp_pool, sizeof(ngx_str_node_t));
    if (sn == NULL) {
        return NGX_ERROR;
    }

    sn->node.key = hash;
    sn->str = cd->name;

    ngx_rbtree_insert(&cf->cycle->config_dump_rbtree, &sn->node);

    return NGX_OK;
}

(4)ngx_conf_add_dump被ngx_conf_parse调用

/src/core/ngx_conf_file.c

char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
// ...
        if (ngx_dump_config
#if (NGX_DEBUG)
            || 1
#endif
           )
        {
            if (ngx_conf_add_dump(cf, filename) != NGX_OK) {
                goto failed;
            }

        } else {
            cf->conf_file->dump = NULL;
        }
// ...
}

(5)红黑树使用流程图:

ngx_rbtree.c
ngx_string.c
ngx_conf_file.c
ngx_rbtree.h
ngx_cycle.c
nginx.c
call
call
call
call
call
call
call
ngx_rbtree_insert()
ngx_str_rbtree_lookup()
ngx_conf_add_dump()
ngx_conf_parse()
ngx_rbtree_init()
ngx_init_cycle()
main()
其他函数

2.2、ngx_open_file_cache

ngx_open_file_cache_t是文件缓存,主要在ngx_http_log_module、ngx_http_core_module、ngx_stream_log_module几个模块中使用。
在/src/core/ngx_open_file_cache.h中定义的数据结构struct ngx_cycle_s引用了红黑树变量:

// ...
struct ngx_cached_open_file_s {
    ngx_rbtree_node_t        node;
    ngx_queue_t              queue;

    u_char                  *name;
    time_t                   created;
    time_t                   accessed;

    ngx_fd_t                 fd;
    ngx_file_uniq_t          uniq;
    time_t                   mtime;
    off_t                    size;
    ngx_err_t                err;

    uint32_t                 uses;

#if (NGX_HAVE_OPENAT)
    size_t                   disable_symlinks_from;
    unsigned                 disable_symlinks:2;
#endif

    unsigned                 count:24;
    unsigned                 close:1;
    unsigned                 use_event:1;

    unsigned                 is_dir:1;
    unsigned                 is_file:1;
    unsigned                 is_link:1;
    unsigned                 is_exec:1;
    unsigned                 is_directio:1;

    ngx_event_t             *event;
};


typedef struct {
    ngx_rbtree_t             rbtree;
    ngx_rbtree_node_t        sentinel;
    ngx_queue_t              expire_queue;

    ngx_uint_t               current;
    ngx_uint_t               max;
    time_t                   inactive;
} ngx_open_file_cache_t;

// ...

(1)红黑树的初始化在ngx_open_file_cache_init函数中调用。
/src /core/ngx_open_file_cache.c

ngx_open_file_cache_t *
ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
{
// ...
    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
                    ngx_open_file_cache_rbtree_insert_value);
// ...
}

(2)ngx_http_log_module模块的ngx_http_log_commands中注册了ngx_http_log_open_file_cache。ngx_http_log_open_file_cache会调用ngx_open_file_cache_init做初始化。

/src/http/modules/ngx_http_log_module.c


static char *
ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{

	// ...

	llcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);

    if (llcf->open_file_cache) {

        llcf->open_file_cache_valid = valid;
        llcf->open_file_cache_min_uses = min_uses;

        return NGX_CONF_OK;
    }

    return NGX_CONF_ERROR;
}

static ngx_command_t  ngx_http_log_commands[] = {

    { ngx_string("log_format"),
      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
      ngx_http_log_set_format,
      NGX_HTTP_MAIN_CONF_OFFSET,
      0,
      NULL },

    { ngx_string("access_log"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                        |NGX_HTTP_LMT_CONF|NGX_CONF_1MORE,
      ngx_http_log_set_log,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

    { ngx_string("open_log_file_cache"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
      ngx_http_log_open_file_cache,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

      ngx_null_command
};

ngx_module_t  ngx_http_log_module = {
    NGX_MODULE_V1,
    &ngx_http_log_module_ctx,              /* module context */
    ngx_http_log_commands,                 /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};


(3)ngx_http_core_module模块的ngx_http_core_commands中注册了ngx_http_core_open_file_cache。ngx_http_core_open_file_cache会调用ngx_open_file_cache_init做初始化。

/src/http/modules/ngx_http_core_module.c

static char *
ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{

	// ...
	
	clcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
    if (clcf->open_file_cache) {
        return NGX_CONF_OK;
    }

    return NGX_CONF_ERROR;
}

static ngx_command_t  ngx_http_core_commands[] = {
	// ...
	{ ngx_string("open_file_cache"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
      ngx_http_core_open_file_cache,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_core_loc_conf_t, open_file_cache),
      NULL },
      
     // ...
}


ngx_module_t  ngx_http_core_module = {
    NGX_MODULE_V1,
    &ngx_http_core_module_ctx,             /* module context */
    ngx_http_core_commands,                /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

(4)ngx_stream_log_module模块的ngx_stream_log_commands中注册了ngx_stream_log_open_file_cache。ngx_stream_log_open_file_cache会调用ngx_open_file_cache_init做初始化。

/src/http/modules/ngx_stream_log_module.c

static char *
ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
	// ...
	
	lscf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);

    if (lscf->open_file_cache) {

        lscf->open_file_cache_valid = valid;
        lscf->open_file_cache_min_uses = min_uses;

        return NGX_CONF_OK;
    }

    return NGX_CONF_ERROR;
}

static ngx_command_t  ngx_stream_log_commands[] = {

    { ngx_string("log_format"),
      NGX_STREAM_MAIN_CONF|NGX_CONF_2MORE,
      ngx_stream_log_set_format,
      NGX_STREAM_MAIN_CONF_OFFSET,
      0,
      NULL },

    { ngx_string("access_log"),
      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
      ngx_stream_log_set_log,
      NGX_STREAM_SRV_CONF_OFFSET,
      0,
      NULL },

    { ngx_string("open_log_file_cache"),
      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1234,
      ngx_stream_log_open_file_cache,
      NGX_STREAM_SRV_CONF_OFFSET,
      0,
      NULL },

      ngx_null_command
};


static ngx_stream_module_t  ngx_stream_log_module_ctx = {
    NULL,                                  /* preconfiguration */
    ngx_stream_log_init,                   /* postconfiguration */

    ngx_stream_log_create_main_conf,       /* create main configuration */
    NULL,                                  /* init main configuration */

    ngx_stream_log_create_srv_conf,        /* create server configuration */
    ngx_stream_log_merge_srv_conf          /* merge server configuration */
};


ngx_module_t  ngx_stream_log_module = {
    NGX_MODULE_V1,
    &ngx_stream_log_module_ctx,            /* module context */
    ngx_stream_log_commands,               /* module directives */
    NGX_STREAM_MODULE,                     /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};


(5)红黑树使用流程图:

ngx_rbtree.c
ngx_stream_log_module.c
ngx_http_core_module.c
ngx_http_log_module.c
ngx_open_file_cache.c
ngx_rbtree.h
注册
注册
注册
注册
注册
注册
注册
call
call
call
call
call
call
call
call
ngx_rbtree_delete()
ngx_rbtree_insert()
ngx_stream_log_open_file_cache()
ngx_command_t ngx_stream_log_commands[]
ngx_module_t ngx_stream_log_module
ngx_http_core_open_file_cache()
ngx_command_t ngx_http_core_commands[]
ngx_module_t ngx_http_core_module
ngx_http_log_open_file_cache()
ngx_command_t ngx_http_log_commands[]
ngx_module_t ngx_http_log_module
ngx_open_file_cache_cleanup()
ngx_open_file_cache_init()
ngx_open_cached_file()
ngx_rbtree_init()
其他函数

总结

Nginx在很多地方使用了红黑树,nginx红黑树的使用基本是这样的流程:

  1. 定义红黑树变量。可以是在单独定义或者作为结构体的成员。
  2. 初始化红黑树,ngx_rbtree_init(),并提供插入value的函数,比如nginx内部实现的ngx_str_rbtree_insert_value() 。
  3. 初始化完成后,就可以在需要的地方调用ngx_rbtree_delete()、ngx_rbtree_insert()等函数。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lion Long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值