nginx之红黑树

来源:互联网 发布:b2b平台如运营 知乎 编辑:程序博客网 时间:2024/05/21 07:57

红黑树上每个节点内含五个域,color,key,left,right,p。如果相应的指针域没有,则设为NIL。

叶子节点即为树尾端的NIL指针,或者说NULL节点。

红黑树需要遵从下面的5条性质:

(1)节点要么是红色要么是黑色;

(2)根节点为黑色;

(3)叶子节点即NIL节点必定为黑色;

(4)红色节点的孩子节点必定为黑色;

(5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点,即黑高度相同;

上面的5条规则,主要是第(4)、(5)两条保证了红黑树的近似完整平衡性。

红黑树的旋转操作

(1)右旋转
(2)左旋转
(3)先左旋再右旋
(4)先右旋再左旋

nginx相关内存

nginx实现的红黑树,包含两个结构体:ngx_rbtree_t和ngx_rbtree_node_t。

struct ngx_rbtree_s {

   ngx_rbtree_node_t     *root;//根节点

   ngx_rbtree_node_t     *sentinel;//设置树的哨兵节点

   ngx_rbtree_insert_pt   insert;//插入方法的函数指针

};

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;//节点颜色,0表示黑色,1表示红色

   u_char                 data;//数据

};

typedef struct

{

         ngx_rbtree_node_tnode;

         ngx_int_t                   num;

         void             *data; //存储数据的指针

}TestRBTNode;//自定义的数据结构体必须包含ngx_rbtree_node_t

//向红黑树中插入节点。

void ngx_rbtree_insert(ngx_thread_volatilengx_rbtree_t *tree, ngx_rbtree_node_t *node);

//在红黑树中删除节点。

void ngx_rbtree_insert_value(ngx_rbtree_node_t*temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)。

插入删除例子

由于ngx_rbtree_t未牵涉到内存池,所以非常容易抽出来使用,如下为实现了插入、打印最小值、删除的例子
    #include <iostream>
    #include <algorithm>
    #include <pthread.h>
    #include <time.h>
    #include <stdio.h>
    #include <errno.h>  
    #include <string.h>
   #include "ngx_queue.h"
    #include "ngx_rbtree.h"
   int main()
   {
       ngx_rbtree_t tree;
       ngx_rbtree_node_t sentinel;
       ngx_rbtree_init(&tree,&sentinel,ngx_rbtree_insert_value);
       ngx_rbtree_node_t *rbnode = new ngx_rbtree_node_t[100];
       for(int i = 99; i >= 0 ;i--)
       {
           rbnode[i].key = i;
           rbnode[i].parent = NULL;
           rbnode[i].left = NULL;
           rbnode[i].right = NULL;
           ngx_rbtree_insert(&tree,&rbnode[i]);
       }
       for(int i = 0; i < 100;i++)
       {
            ngx_rbtree_node_t *p = ngx_rbtree_min(tree.root,&sentinel);
            std::cout << p->key << "  ";
          ngx_rbtree_delete(&tree,p);
        }
        delete[] rbnode;
        return 0;
   }


添加遍历例子

#include <stdio.h>

#include <stdlib.h>

#include <ngx_core.h>

#include <ngx_config.h>

#include <ngx_conf_file.h>

#include <ngx_string.h>

#include <ngx_palloc.h>

#include <ngx_rbtree.h>

 

#define ngx_rbtee_data(p, type, member)(type*)((u_char*)p - offset(type, member))

 

//节点数据结构

typedef struct

{

  ngx_rbtree_node_t  node;//方便类型转化

  ngx_str_t             str;

}ngx_string_node_t;

 

//自定义插入函数

void ngx_string_rbtree_insert_value(ngx_rbtree_node_t*temp, ngx_rbtree_node_t *node,

  ngx_rbtree_node_t*sentinel);

 

//先序遍历红黑树

void trave_rbtree(ngx_rbtree_node_t *root,ngx_rbtree_node_t *sentinel);

 

int main()

{

  ngx_rbtree_t   tree;

  ngx_rbtree_node_tsentinel;

 

  ngx_rbtree_init(&tree,&sentinel, ngx_string_rbtree_insert_value);

 

  ngx_string_node_tstrNode[10];

 

  ngx_str_set(&strNode[0].str,"abc0");

  strNode[0].node.key= 1;

 

  ngx_str_set(&strNode[1].str,"abc1");

  strNode[1].node.key= 6;

 

  ngx_str_set(&strNode[2].str,"abc2");

  strNode[2].node.key= 8;

 

  ngx_str_set(&strNode[3].str,"abc35");

  strNode[3].node.key= 11;

 

  ngx_str_set(&strNode[4].str,"abd4");

  strNode[4].node.key= 8;

 

  ngx_str_set(&strNode[5].str,"abc5");

  strNode[5].node.key= 1;

 

  ngx_str_set(&strNode[6].str,"abc11");

  strNode[6].node.key= 11;

 

  ngx_str_set(&strNode[7].str,"a6");

  strNode[7].node.key= 1;

 

  ngx_str_set(&strNode[8].str,"a8");

  strNode[8].node.key= 6;

 

  ngx_str_set(&strNode[9].str,"abc0");

  strNode[9].node.key= 6;

 

  ngx_int_t i;

  for (i = 0; i< 10; ++i)

  {

    ngx_rbtree_insert(&tree,&strNode[i].node);

  }

 

  travel_rbtree(tree.root,tree.sentinel);

 

  return 0;

}

 

void

ngx_string_rbtree_insert_value(ngx_rbtree_node_t *temp,ngx_rbtree_node_t *node,

  ngx_rbtree_node_t*sentinel)

{

  ngx_rbtree_node_t  **p;

  ngx_string_node_t     *strNodeX;

  ngx_string_node_t     *strNodeY;

 

  for ( ;; )

  {

    if(node->key != temp->key)

    {

       p = (node->key < temp->key) ?&temp->left : &temp->right;

    }

    else

    {

      strNodeX =(ngx_string_node_t*)node;//类型转化,因为node的地址和ngx_string_node_t的地址相同

      //strNodeY =(ngx_string_node_t*)temp;

      //或者这样用

      strNodeY =ngx_rbtree_data(temp, ngx_string_node_t, node);

 

      if(strNodeX->str.len != strNodeY->str.len)

      {

        p =(strNodeX->str.len < strNodeY->str.len) ? &temp->left :&temp->right;

      }

      else

      {

        p =(ngx_memcmp(strNodeX->str.data, strNodeY->str.data, strNodeX->str.len)< 0) ? &temp->left : &temp->right;

      }

    }

    if (*p ==sentinel)

    {

      break;

    }

 

    temp = *p;

  }

  //每一个待插入的节点必须初始化为红色

  *p = node;

  node->parent =temp;//初始化父节点

  node->left =sentinel;//初始化左子节点

  node->right =sentinel;//初始化右子节点

  ngx_rbt_red(node);//颜色设置为红色

}

 

void

travel_rbtree(ngx_rbtree_node_t* root, ngx_rbtree_node_t*sentinel)

{

  if (root->left!= sentinel)

  {

    travel_rbtree(root->left,sentinel);

  }

 

  ngx_string_node_t*strNode = (ngx_string_node_t*)root;

  printf("key= %d , str=%s\n", root->key, strNode->str.data);

 

  if(root->right != sentinel)

  {

    travel_rbtree(root->right,sentinel);

  }

}

nginx例子

nginx对静态文件cache的处理
Nginx中对静态文件进行了Cache,所有的cache对象包含在两个数据结构里面,整个机制最关键的也是这两个东西,一个是红黑树,一个是一个队列,其中红黑树是为了方便查找(需要根

据文件名迅速得到fd),而队列为了方便超时管理(按照读取顺序插入,在头的就是最近存取的文件),由于所有的句柄的超时时间都是一样的,因此每次只需要判断最后几个元素就够了,因为这个超时不需要那么实时。
首先,这里,cache的红黑树的key是一个hash值,是文件名的crc校验码:
//计算hash
    hash = ngx_crc32_long(name->data, name->len);
//根据名字查找对应的file对象。
    file = ngx_open_file_lookup(cache, name, hash);
首先我们来看如果没有找到cache对象的情况,此时nginx会open 这个文件,然后保存对应的信息到cache中。
然后就是更新队列的部分,这里可以看到插入是每次都插入到队列的头,之所以插入到头,是为了超时操作更方便。

0 0