nginx--rbtree使用详解

来源:互联网 发布:js截取字符串的方法 编辑:程序博客网 时间:2024/05/02 01:54

      红黑数的性质这里就不多说了网上很多,主要看看nginx是如何定义的rbtree和怎么使用它

      

rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));   //从共享内存中创建一颗红黑树sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));   //创建了一个红黑树的节点作为哨兵节点ngx_rbtree_init(ctx->rbtree, ctx->sentinel, ngx_http_srs_hook_rbtree_insert_value); //初始化这颗红黑树

主要看下ngx_rbtree_init的实现:

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

关键的就是参数i,这个是个函数指针,作为红黑树的插入节点时候的算法,这个需要用户自己实现

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

第二个参数就是需要插入的节点数据,第三个参数是哨兵节点

接下去看下怎么实现这个插入函数(ps: nginx很多模块中已经有了rbtree插入的实现,我也是参考写的)

static void ngx_http_srs_hook_rbtree_insert_value(ngx_rbtree_node_t *temp,   ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) {ngx_rbtree_node_t           **p;    p = NULL;    if(node == NULL) {        return;    }    for ( ;; ) {        if (node->key < temp->key) {            p = &temp->left;        } else if (node->key > temp->key) {            p = &temp->right;        } else { /* node->key == temp->key */            break;  //目前的实现不支持相同的key的节点        }        if (*p == sentinel) {            break;        }        temp = *p;    }    *p = node;    node->parent = temp;    node->left = sentinel;    node->right = sentinel;    ngx_rbt_red(node);      //最后就是要让插入的节点是红色的(这是红黑树的性质) }

对于使用者只需要负责插入的算法, 对于红黑数的反转等问题nginx都为大家做了


现在看看怎么进行节点的插入的

typedef struct{                  ngx_rbtree_node_t node;    void *data;}channel;     //定义的红黑树节点channel findchannel;ngx_rbtree_insert(srshookctx->rbtree,&(findchannel->node));

看下ngx_rbtree_insert的实现:

ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node){    ngx_rbtree_node_t  **root, *temp, *sentinel;    /* a binary tree insert */    root = (ngx_rbtree_node_t **) &tree->root;    sentinel = tree->sentinel;    if (*root == sentinel) {        node->parent = NULL;        node->left = sentinel;        node->right = sentinel;        ngx_rbt_black(node);        *root = node;        return;    }    tree->insert(*root, node, sentinel);     //这里就调用了我们之前设定的插入算法的函数    /* re-balance tree */                    //数据插入之后, nginx就为我们做了很复杂的红黑树的翻转来保持树的平衡    while (node != *root && ngx_rbt_is_red(node->parent)) {        if (node->parent == node->parent->parent->left) {            temp = node->parent->parent->right;            if (ngx_rbt_is_red(temp)) {                ngx_rbt_black(node->parent);                ngx_rbt_black(temp);                ngx_rbt_red(node->parent->parent);                node = node->parent->parent;            } else {                if (node == node->parent->right) {                    node = node->parent;                    ngx_rbtree_left_rotate(root, sentinel, node);                }                ngx_rbt_black(node->parent);                ngx_rbt_red(node->parent->parent);                ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);            }        } else {            temp = node->parent->parent->left;            if (ngx_rbt_is_red(temp)) {                ngx_rbt_black(node->parent);                ngx_rbt_black(temp);                ngx_rbt_red(node->parent->parent);                node = node->parent->parent;            } else {                if (node == node->parent->left) {                    node = node->parent;                    ngx_rbtree_right_rotate(root, sentinel, node);                }                ngx_rbt_black(node->parent);                ngx_rbt_red(node->parent->parent);                ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);            }        }    }    ngx_rbt_black(*root);}

这样红黑数的插入就完成了,那如何来查找树里的节点呢?这里我实现了一个查找节点的函数

static requestchannel_st*ngx_http_channel_tree_lookup(ngx_http_srs_hook_ctx_t *srsctx, uint64_t key){    ngx_rbtree_key_t             node_key;    ngx_rbtree_node_t           *node;    ngx_rbtree_node_t           *sentinel;    requestchannel_st                  *fcn;    node_key = key;    node = srsctx->rbtree->root;      //这里记录下要查找的树的根节点    sentinel = srsctx->rbtree->sentinel;  //记录下哨兵节点    while (node != sentinel) {        if (node_key < node->key) {       //通过左右子树查找key一样的节点            node = node->left;            continue;        }        if (node_key > node->key) {            node = node->right;            continue;        }        /* node_key == node->key */        fcn = (requestchannel_st *) node;    //找到节点就转成节点数据结构,返回        return fcn;    }    /* not found */    return NULL;}



0 0