文档章节

nginx源码分析之模块化

那一剑的风情
 那一剑的风情
发布于 2012/09/20 10:21
字数 1213
阅读 3.8K
收藏 43

精选30+云产品,助力企业轻松上云!>>>


模块化编程是C的核心思想,而nginx将这一思想发挥到淋漓尽致。

在阅读源码之前,自己动手写一个模块是最好的入门方式,本文将引导读者如何写一模块,
并分析nginx是如何设计模块化的。

我将以hello world为例,实现访问 http://yourdomain/hello 时,页面上将出现 hello world

1、创建一个目录: hello
2、创建源文件: ngx_http_hello_module.c
3、创建模块配置文件: config
4、创建测试nginx配置文件:nginx.conf

假设放在/tmp下,代码层次如下:
/tmp/hello
        ngx_http_hello_module.c
        config
        nginx.conf

4、将模块编进nginx: ./configure --add-module=/tmp/hello
输出包含如下信息,表示成功
configuring additional modules
adding module in /tmp/hello
 +  was configured


5、开始实现
在实现之前,先分析下模块化在nginx里面是如何设计和实现的。

首先,每个模块就是一个实例,nginx由众多模块组成,协同作用,共同完成服务。
比如 ngx_core_module, ngx_epoll_module, ngx_http_log_module, 还有我们的 ngx_http_hello_module。
这个实例变量,相当于一个形象代言人,是个极具丰富的模型。

每个项目都有配置文件,nginx也不例外。配置文件有多个配置选项,这些配置选项丰富了nginx的服务行为。比如
daemon            on;  # 以守护进程方式启动
worker_processes  4; # 指定工作进程的数量为4

nginx在解析和处理配置文件的方式非常高明
先看下结构体
/* 模块结构体 */
ngx_module_t {
    void                      *ctx;          /* 模块上下文 */
    ngx_command_t    *commands;   /* 指令集 */
}

/* 指令结构体 */
ngx_command_t {
    ngx_str_t             name;        /* 指令名称,用来匹配配置选项的 */         
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); /* 指定如何将配置选项值放到模块配置结构体的 */
}

ngx_xxx_module_t : 模块上下文结构体,有4种
                   ngx_core_module_t, ngx_event_module_t, ngx_http_module_t, ngx_mail_module_t
                   以ngx_core_module_t为例
typedef struct {
    ngx_str_t     name;
    void       *(*create_conf)(ngx_cycle_t *cycle);                /* 创建模块配置结构体 */
    char       *(*init_conf)(ngx_cycle_t *cycle, void *conf);    /* 配置选项解析后的处理 */
} ngx_core_module_t;

ngx_xxx_conf_t     : 模块配置结构体,每个模块都有自己的一个,比如 ngx_core_conf_t,以ngx_core_conf_t为例:
ngx_core_conf_t {
     ngx_flag_t   daemon;                   /* 是否以守护进程启动 */
     ngx_int_t    worker_processes;     /* 工作进程数 */
}

这4个重要的结构体,所做的事情就是将配置文件里值,能让程序代码里轻松的获取配置选项的值。
其中 ngx_module_t, ngx_command_t, ngx_xxx_module_t互相配合,共同产生 ngx_xxx_conf_t。

比如nginx是如何实现是否以守护进程方式启动的,代码如下:
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
if ( ccf->daemon) {
    if (ngx_daemon(cycle->log) != NGX_OK) {
        return 1;
    }
}

注:nginx配置是具有层次的,尤其以http更为复杂,本文只让您明白模块化的大体原理,不作所有的讲解。

清楚nginx如何设计后,我们开始实现ngx_http_hello_module。

直接上代码,请看代码里的注释,我将标出步骤
ngx_http_hello_module.c:
/*
 * Copyright (C) FatHong
 */


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


/* 5、配置结构体,这个专属于 ngx_http_hello_module */
typedef struct {
    ngx_str_t    test;          /* test选项的值将存储在这里 */
} ngx_http_hello_loc_conf_t;


static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_hello(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);


/* 4、指令集 */
static ngx_command_t ngx_http_hello_commands[] = {
    { ngx_string("hello"),                  /* hello指令,对应配置选项 hello; */
      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
      ngx_http_hello,
      0,
      0,
      NULL },

    { ngx_string("test"),                   /* test指令,对应配置选项 test  "hello world"; */
      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_str_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_hello_loc_conf_t, test),    /* test选项的值将存储在这里 */
      NULL },

    ngx_null_command
};


/* 2、模块上下文,这里是http类型模块 */
static ngx_http_module_t ngx_http_hello_module_ctx = {
    NULL,                              /* preconfiguration */
    NULL,                              /* postconfiguration */

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

    NULL,                              /* create server configuration */
    NULL,                              /* merge server configuration */

    ngx_http_hello_create_loc_conf,    /* create location configuration:3.1、配置结构体钩子注册 */
    NULL                               /* merge location configuration */
};


/* 1、模块创建 */
ngx_module_t ngx_http_hello_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_module_ctx,    /* module context:模块上下文 */
    ngx_http_hello_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
};

static u_char ngx_hello_string[] = "hello world";

/* 处理函数 */
static ngx_int_t
ngx_http_hello_handler(ngx_http_request_t *r)
{
    ngx_int_t    rc;
    ngx_buf_t   *b;
    ngx_chain_t  out;
    ngx_http_hello_loc_conf_t *hrcf;

    /* 设置响应内容格式,这里为text/html */
    r->headers_out.content_type_len = sizeof("text/html") - 1;
    r->headers_out.content_type.len = sizeof("text/html") - 1;
    r->headers_out.content_type.data = (u_char *) "text/html";

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    if (b == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    out.buf = b;
    out.next = NULL;

    b->pos = ngx_hello_string;
    b->last = ngx_hello_string + sizeof(ngx_hello_string) - 1;
    b->memory = 1;
    b->last_buf = 1;

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = sizeof(ngx_hello_string) - 1;

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    /* 演示如何获取配置选项的值 */
    hrcf = ngx_http_get_module_loc_conf(r, ngx_http_hello_module);
    printf("test: %s\n", hrcf->test.data); // 直接输出到标准输出

    return ngx_http_output_filter(r, &out);
}


/* 3.2 配置结构体就是这样产生的 */
static void *
ngx_http_hello_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_hello_loc_conf_t  *hlcf;

    hlcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t));
    if (hlcf == NULL) {
        return NULL;
    }

    return hlcf;
}


static char *
ngx_http_hello(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_hello_handler; /* 相当于一个钩子 */

    return NGX_CONF_OK;
}

config:
ngx_addon_name=ngx_http_hello_module
HTTP_MODULES="$HTTP_MODULES ngx_http_hello_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_module.c"


nginx.conf:

worker_processes 4;
daemon  off;

events {
    worker_connections 1024;
}

http {
     server {
        location / {
            hello;
            test  "hello world"; # 这个指令用于演示配置结构体
        }
     }
}


> ./configure --add-module=/tmp/hello
> make
> ./objs/nginx -c /tmp/hello/nginx.conf

访问 http://yourdomain/hello
页面出现 hello world

并且守护进程输出 test: hello world

本文结束,阅读原文:http://nglua.com/reads/2.html

 


那一剑的风情

那一剑的风情

粉丝 122
博文 20
码字总数 21879
作品 0
厦门
程序员
私信 提问
加载中
此博客有 10 条评论,请先登录后再查看。
nginx源码分析之配置图解

nginx配置结构清晰,层次分明,这得益于整个架构的模块化设计,文本将揭示配置文件如何被处理和应用。 整个配置文件解析后的结果如图这样存储。 一、解析的核心机制 nginx源码里,ngxconft是...

那一剑的风情
2012/09/28
3.4K
1
Nginx架构分析(20200202)

Nginx模块化 Nginx基于模块化设计,每个模块是一个功能实现,分布式开发,团队协作 核心模块、标准HTTP模块、可选HTTP模块、邮件模块、第三方模块 编译后的源码目录objs/ngx_modules.c http...

osc_nj4g7wto
02/05
2
0
nginx源码分析——模块

模块介绍 高度模块化的设计是nginx的架构基础。在nginx中,除了少量的核心代码,其他一切皆为模块。这种模块化设计同时具有以下几个特点: 高度抽象的模块接口 所有的模块都遵循着同样的 ng...

hncscwc
2016/06/17
201
0
nginx源码分析(2)- 概览

源码分析是一个逐步取精的过程,最开始是一个大概了解的过程,各种认识不会太深刻,但是把这些真实的感受也记录下来,觉得挺有意思的,可能有些认识是片面或者是不正确的,但可以通过后面更深...

JoyGao
2013/03/29
17
0
nginx源码学习

nginx的优秀除了体现在程序结构以及代码风格上,nginx的源码组织也同样简洁明了,目录结构层次结构清晰,值得我们去学习。nginx的源码目录与nginx的模块化以及功能的划分是紧密结合,这也使得...

fang_faye
2019/09/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

使当前提交成为Git存储库中唯一的(初始)提交? - Make the current commit the only (initial) commit in a Git repository?

问题: I currently have a local Git repository, which I push to a Github repository. 我目前有一个本地Git存储库,我将其推送到Github存储库。 The local repository has ~10 commits, ......

javail
42分钟前
14
0
IntelliJ IDEA 默认快捷键大全

Remember these Shortcuts 常用 功能 快捷键 备注 ● Smart code completion Ctrl + Shift + Space - ● Search everywhere Double Shift - ● Show intention actions and quick-fixes Alt......

巨輪
今天
24
0
Hacker News 简讯 2020-07-14

更新时间: 2020-07-14 02:01 Chipmaker Analog Devices to Acquire Maxim Integrated for $21B - (reuters.com) 芯片制造商模拟设备公司将以210亿美元收购Maxim Integrated 得分:92 | 评论:......

FalconChen
今天
129
0
绕过移动端系统限制的 dlopen 库 byOpen

byOpen是一个绕过移动端系统限制的增强版dlfunctions库。 支持特性 Android 支持App中加载和使用Android系统库接口(即使maps中还没有被加载也支持)。 Android 7以上dlopen, System.load都是...

shzwork
昨天
31
0
Golang学习系列第二天:变量、常量、数据类型和流程语句

继golang第一天后,今天学习下golang的变量、常量、数据类型和控制流语句。 做过其他编程语言(比如JavaScript,java,python)项目的话,其实很好理解变量、常量、数据类型和控制流。 变量也...

董广明
昨天
48
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部