`
xiqiao1229
  • 浏览: 12749 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

Ningx代码研究(四)

阅读更多

虽然代码理解起来比较混乱,但是使用还是比较简单的,常用的有创建 hash 和在 hash 中进行查找两个操作,对于创建hash的操作,过程一般为:

  1. 构造一个 ngx_hash_key_t 为成员的数组, 包含 key, value 和 使用key计算出的一个hash值
  2. 构建一个 ngx_hash_init_t结构体的变量, 其中包含了ngx_hash_t 的成员, 为hash的结构体, 还包括一些其他初始设置,如bucket的大小,内存池等
  3. 调用 ngx_hash_init 传入 ngx_hash_init_t 结构, ngx_hash_key_t 的数组,和数组的长度, 进行初始化,这样 ngx_hash_init_t的hash成员就是我们要的hash结构

查找的过程很简单

  1. 计算 key 的hash值
  2. 使用 ngx_hash_find 进行查找,需要同时传入 hash值和key ,返回的就是value的指针

 

需要注意的是,nginx 的 hash 在查找时使用的是分桶后线性查找法,因此当分桶数确定时查找效率同其中的总 key-val 对数量成反比。

下面是一些demo代码(可以从svn中找到)

#include <stdio.h>
#include "ngx_config.h"
#include "ngx_conf_file.h"
#include "nginx.h"
#include "ngx_core.h"
#include "ngx_string.h"
#include "ngx_palloc.h"
#include "ngx_array.h"
#include "ngx_hash.h"
volatile ngx_cycle_t  *ngx_cycle;
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...) { }


static ngx_str_t names[] = {ngx_string("rainx"),
                            ngx_string("xiaozhe"),
                            ngx_string("zhoujian")};
static char* descs[] = {"rainx's id is 1","xiaozhe's id is 2","zhoujian's id is 3"};

// hash table的一些基本操作
int main()
{
    ngx_uint_t          k; //, p, h;
    ngx_pool_t*         pool;
    ngx_hash_init_t     hash_init;
    ngx_hash_t*         hash;
    ngx_array_t*        elements;
    ngx_hash_key_t*     arr_node;
    char*               find;
    int                 i;

    ngx_cacheline_size = 32;
    // hash key cal start
    ngx_str_t str       = ngx_string("hello, world");
    k                   = ngx_hash_key_lc( str.data, str.len);
    pool                = ngx_create_pool(1024*10, NULL);
    printf("caculated key is %u \n", k);
    // hask key cal end
    //
    hash = (ngx_hash_t*) ngx_pcalloc(pool, sizeof(hash));
    hash_init.hash      = hash;                      // hash结构
    hash_init.key       = &ngx_hash_key_lc;          // hash算法函数
    hash_init.max_size  = 1024*10;                   // max_size
    hash_init.bucket_size = 64; // ngx_align(64, ngx_cacheline_size);
    hash_init.name      = "yahoo_guy_hash";          // 在log里会用到
    hash_init.pool           = pool;                 // 内存池
    hash_init.temp_pool      = NULL;

    // 创建数组

    elements = ngx_array_create(pool, 32, sizeof(ngx_hash_key_t));
    for(i = 0; i < 3; i++) {
        arr_node            = (ngx_hash_key_t*) ngx_array_push(elements);
        arr_node->key       = (names[i]);
        arr_node->key_hash  = ngx_hash_key_lc(arr_node->key.data, arr_node->key.len);
        arr_node->value     = (void*) descs[i];
        // 
        printf("key: %s , key_hash: %u\n", arr_node->key.data, arr_node->key_hash);
    }

    if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) elements->elts, elements->nelts)!=NGX_OK){
        return 1;
    }

    // 查找
    k    = ngx_hash_key_lc(names[0].data, names[0].len);
    printf("%s key is %d\n", names[0].data, k);
    find = (char*)
        ngx_hash_find(hash, k, (u_char*) names[0].data, names[0].len);

    if (find) {
        printf("get desc of rainx: %s\n", (char*) find);
    }

    ngx_array_destroy(elements);
    ngx_destroy_pool(pool);

    return 0;
}

运行结果

rainx@rainx-laptop:~/land/nginxsrp/src/demo/basic_types$ ./hash_op 
caculated key is 3654358412 
key: rainx , key_hash: 108275556
key: xiaozhe , key_hash: 2225329080
key: zhoujian , key_hash: 3269715264
rainx key is 108275556
get desc of rainx: rainx's id is 1

ngx_list

ngx_list 的结构并不复杂,ngx为我们封装了ngx_list_create, ngx_list_init, 和 ngx_list_push等(建立,初始化,添加)操作, 但是对于我们来说最常用的是遍历操作, 下面是nginx的注释里面提到的遍历的例子

   part = &list.part;
   data = part->elts;
 
   for (i = 0 ;; i++) {
 
       if (i >= part->nelts) {
           if (part->next == NULL) {
               break;
           }
 
           part = part->next;
           data = part->elts;
           i = 0;
       }
 
       ...  data[i] ...
 
   }

了解nginx的core module 的结构和运行机制

参考资料

在开始这个task的学习的时候,经过搜索发现了langwan同学之前对nginx的源代码研究资料,很有参考意义,所以大量节省了我们的工作,我觉得对于本章的进行比较有用的是,下面这几个文章

  1. nginx源代码分析 http://hi.baidu.com/langwan/blog/item/6b18ef24cd859e064c088d28.html
  2. nginx 缓冲区构造 http://hi.baidu.com/langwan/blog/item/822b758d5d1d9a1ab31bbaf8.html
  3. Nginx源代码分析 - 日志处理 http://hi.baidu.com/langwan/blog/item/7e7db51978e04e4d43a9ad32.html

Debug信息的输出

为了方便研究,将nginx的debug 信息打开,重新编译

rainx@rainx-laptop:~/land/nginx-0.7.61$ ./configure --prefix=/home/rainx/land/test --with-debug

然后修改nginx.conf

worker_processes  2; 
error_log  logs/error.log  debug;

打开debug信息的支持,并使用2个worker进程,通过查看 log 信息来了解 nginx 运行的情况

基于上面的配置信息,结合一个简单的http访问操作,我这里记录了一个 log日志的例子

ngx_init_cycle

其中一个比较重要的函数调用是, ngx_init_cycle, 这个是使用kscope输出的他的调用关系,他被main, ngx_master_process_cycle,ngx_single_process_cycle 调用, 其中后两者是在reconfigure的时候被调用的

他主要做了如下几件事情:

初始化cycle是基于旧有的cycle进行的,比如这里的 init_cycle,会继承old cycle的很多属性, 比如log等, 但是同时会对很多资源重新分配,比如pool, shared mem, file handler, listening socket 等,同时清除旧有的cycle的资源 

另外,ngx_master/single_process_cycle 里面会对init_process进行调用, 并且循环调用 ngx_process_events_and_timers , 其中里面会调用ngx_process_events(cycle, timer, flags); 对事件循环进行polliing 时间一般默认为 500 ms

了解nginx的http core module 的结构和运行机制

HTTP相关的Module都在 src/http 目录和其子目录下, 其中 src/http 下的文件为http模块的核心文件, src/http/modules 下的文件为http模块的扩展模块。

其中:

ngx_http.[c|h]

ngx_http.c 中,注册了 http 这个指令的处理模块,对应ngx_http_block函数

static ngx_command_t  ngx_http_commands[] = {

    { ngx_string("http"),
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_http_block,
      0,
      0,
      NULL },

      ngx_null_command
};

这个函数里面会进行一些conf资源分配/Merge,配置文件解析等工作。 这里面有个一比较重要的工作是注册了nginx http 的 phase handler

    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

phase handler的类型在 ngx_http_core_module 这里定义:

typedef enum {
    NGX_HTTP_POST_READ_PHASE = 0,

    NGX_HTTP_SERVER_REWRITE_PHASE,

    NGX_HTTP_FIND_CONFIG_PHASE,
    NGX_HTTP_REWRITE_PHASE,
    NGX_HTTP_POST_REWRITE_PHASE,

    NGX_HTTP_PREACCESS_PHASE,

    NGX_HTTP_ACCESS_PHASE,
    NGX_HTTP_POST_ACCESS_PHASE,

    NGX_HTTP_TRY_FILES_PHASE,
    NGX_HTTP_CONTENT_PHASE,

    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

每一个phase的handlers 都是一个数组,里面可以包含多个元素,通过 ngx_array_push 添加新的handler

其中每个phase的处理大都包含了对ngx_request_t 的 write 或者 read event的改写,其中

在 ngx_http_core_content_phase 里面, 有对location handler的调用, 其中的 r->content_handler 就是运行时刻从location handler中注册的,

    if (r->content_handler) {
        r->write_event_handler = ngx_http_request_empty_handler;
        ngx_http_finalize_request(r, r->content_handler(r)); /*实际的请求发送处理*/
        return NGX_OK;
    }

其中, 在各个phase的结束阶段,一般都是调用

    r->phase_handler++;
    return NGX_AGAIN;

移动request 中 phase_handler的指针,并且示意主程序继续进行。

这里,无论是phase handler,还是 location handler,我们都是可以在程序里进行注册的。

另外, ngx_http_block 里面调用了 ngx_http_optimize_servers ,这个函数对listening和connection相关的变量进行了初始化和调优,并最终在 ngx_http_add_listening (被ngx_http_add_listening调用) 中注册了listening 的 handler 为 ngx_http_init_connection

    ls->handler = ngx_http_init_connection;

ngx_http_init_connection 在 ngx_http_request.c中定义,后续会进行详细的介绍

ngx_http_request.[c|h]

这里面,ngx_http_init_connection 注册了connection事件的读操作的回叫函数, 并将写操作设置为空函数

    rev = c->read;
    rev->handler = ngx_http_init_request;
    c->write->handler = ngx_http_empty_handler;

当新的连接进入的时候,就执行到 ngx_http_init_request, 开始对后面的流程进行处理,主要是将rev的handler 设置为ngx_http_process_request_line , 然后ngx_http_process_request_line 会先后有调度到 ngx_http_process_request_headers 和 ngx_http_process_request 函数对读取过来的event进行处理,其中, ngx_http_process_request_headers 里面会对http的请求头进行解析,ngx_http_process_request 设置event handler 到ngx_http_request_handler ,ngx_http_request_handler 中会根据事件可能是读取还是写入的操作分别调用 request 的 read_event_handler 和 write_event_handler ,所以后续程序对 request 的 read/writeevent_handler 调整 本质上类似对 rev 和 wev的handler的调整,只是回叫函数的参数变更为了 ngx_request_t 而不是之前的ngx_event_t

    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;
    r->read_event_handler = ngx_http_block_reading;

根据上面代码可以看出, 模块开始使用 ngx_http_block_reading 这个handler对后续的读请求进行处理

在注册完事件后, ngx_http_process_request 会分别调用下面的两个函数

    ngx_http_handler(r);
    ngx_http_run_posted_requests(c);

其中, ngx_http_handler 在ngx_http_core_module中定义,处理程序的主请求, ngx_http_run_posted_requests 在ngx_http_request.c 里定义,处理所有提交的子请求数据的输出。

ngx_http_core_module.[c|h]

对于 ngx_http_core_module 是http 模块中比较重要的模块, 他本身是一个 NGX_HTTP_MODULE (不同于ngx_http_module, ngx_http_module本质上是一个 NGX_CORE_MODULE

这里面对http block下面的一些指令进行了处理, 比如 server, location 等, 同时, 上面提到的 ngx_http_handler 也在这里面

ngx_http_handler 所作的最核心的工作就是在最后调用 并将 write event 设置为 ngx_http_core_run_phases, 开始依次处理各个阶段的 handler

当handler处理完成后,http的处理流程也就基本上完成了..

    while (ph[r->phase_handler].checker) {

        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        if (rc == NGX_OK) {
            return;
        }
    }

run_phases 的过程实际上非常简单, 一次的运行每一个handler, 当任意一个handler返回ok或者所有handler执行完成后,整个流程结束。

这里需要注意的是, ph的下标变化是根据 r->phase_handler 变量决定的, 所以在每个handler内部,如果想要让主程序继续处理下一个 handler,需要手动的 r->phase_handler++ ,将phase handler数组的下标转移到下一个成员。


分享到:
评论

相关推荐

    Nginx 代码研究

    我们发现目前学习nginx的例子很少,主要是emiller的模块开发介绍这篇文章, 但是单独研究这篇文章发现很多晦涩难懂的地方,而目前还没有其他更好的文章来对这些地方做解释, 有些东西必须要通过源代码的研读才可以...

    NGINX 0.1版代码

    这是NGINX的0.1版源代码,模块结构相对简单,适用于有志研究NGINX代码的童鞋进行学习。

    nginx 各个历史版本

    nginx各个阶段的历史版本源码,从0.1版本开始,可以用于研究nginx的代码框架

    nginx 源码 解析 资料大全

    收集的 nginx 源码 解析 ,非常全 architecture.png Emiller的Nginx模块开发指南.docx Nginx(en).pdf nginx@taobao.pdf nginx_internals.pdf nginx核心讲解(0.2).doc nginx核心讲解(0.4).doc ...Ningx代码研究.docx

    nginx核心讲解

    慕名对nginx的源码进行学习研究是早在2009年的事情,当时还在学校,整天呆在实验室里看动漫,时间一久就心感愧疚,觉得还是要趁有空学点东西,恰当时不知从哪里得知高性能服务器是一个很有“前途”的方向,几经搜索...

    Nginx经典模块

    为什么发现了这个插件,因为这几天公司需要在所有shtml文件后面追加一个js代码用来做统计(之前统计 代码没加齐全),在寻求解决方法的过程中找到了它认识了它最后喜欢上了它,你可能以为我用这个插件去实现了我要的...

    Nginx源码研究之nginx限流模块详解

    主要介绍了Nginx源码研究之nginx限流模块详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    nginx源码vs2008版

    nginx最近是越来越火,许多大型商业网站都用它作服务器,作为开发人员很有必要拿来研究一下。从网上下来的都是make版,代码看起来不是那么方便,特别是对我这种习惯vs的程序员,幸好有朋友整理了一下,在vs2008下,...

    Nginx配置实现下载文件的示例代码

    所以今天就想研究下nginx实现文件的上传下载,直接开搞,本地服务启起。这里记录下配置及踩坑记录。 一、配置 http { ... server: { # 配置下载 location /download { root D:\\download; autoindex on; ...

    详解Nginx服务器的nginx-http-footer-filter模块配置

    为什么发现了这个插件,因为这几天公司需要在所有shtml文件后面追加一个js代码用来做统计(之前统计代码没加齐全),在寻求解决方法的过程中找到了它认识了它最后喜欢上了它,你可能以为我用这个插件去实现了我要的...

    Linux上搭建nginx,及简单配置

    在上家公司都是运维安装nginx,到新公司后代码开发完成部署测试服务器要求自己装nginx,研究了好久安装好之后,到正式上线还要自己安装,索性把安装步骤自己记载下来(好大一部分都是在网站找的)。  一,安装  ...

    解析CI即CodeIgniter框架在Nginx下的重写规则

    最近研究CI框架,发现这个框架的路由功能在Nginx下有问题,报404错误,后来在网上查资料,发现需要开启PATH_INFO。在nginx7.16以后貌似就支持PATH_INFO了,只需要在配置文件中开启即可。打开nginx.conf文件,在你的...

    交易所源码分享,原生安卓+ios,有安装教程!

    项目所需软件 (推荐使用宝塔安装) Nginx、php、mysql、redis、supervisor 进行部署项目 以下安装步骤仅以宝塔环境...仅限用于学习和研究目的,不得将上述内容资源用于商业或者非法用途,否则,一切后果请用户自负。

    nginx try_files指令判断文件是否存在实例

    现在有这样一个需求,网站根目录下有静态文件,static目录下也有静态文件,static目录下的静态文件是程序批量生成的,我想让nginx在地址不变的前提下优先使用static目录里面的文件,如果不存在再使用根目录下的静态...

    Answer-Prediction:代码研究员小组项目

    一个代码研究员小组项目,由四个学生在一周内完成。 Sherlock Engine是一个基于Python的应用程序,它使用多元线性回归来预测用户对下一个问题的答案。 每次用户提交答案时,对于该用户以及所有其他用户,所有后续...

    openresty_dev:OpenResty开发指南代码

    openresty_dev 这是OpenResty编程的示例代码。...注释nginx,学习研究原始码 选定的收藏夹nginx模块和资源。 带有google_perftools的nginx更好的工具 -Boost库指南的示例代码。 专业促进发展。

    PHP统计nginx访问日志中的搜索引擎抓取404链接页面路径

    我在服务器上有每天切割nginx日志的习惯,所以针对每天各大搜索引擎来访,总能记录一些404页面信息,传统上我只是偶尔分析下日志,但是对于很多日志信息的朋友,人工来筛选可能不是一件容易的事情,这不我个人自己...

    外贸网站屏蔽中国IP访问的多种方法

    大家都知道的原因,做外贸站,国人喜欢研究你的站,还总是帮你进行压力测试…… 首先想到要屏蔽中国IP就会是把中国IP库加入Nginx配置文件中,然后WEB服务器对比IP来达到屏蔽。 代码如下: 在Nginx中加deny IP; 批量...

    基于Vue + axios + vuex + webpack + koa + mysql + nginx + Lin.zip

    对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同...

Global site tag (gtag.js) - Google Analytics