文档章节

nginx源码分析之模块化

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

模块化编程是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

 


© 著作权归作者所有

共有 人打赏支持
那一剑的风情

那一剑的风情

粉丝 120
博文 20
码字总数 21879
作品 0
厦门
程序员
私信 提问
加载中

评论(10)

f
fan_cheng
不错, 搞了很久。还是你这个能用,不错!
yuekaizhong
yuekaizhong
虽然看不懂,但是感觉很强大,膜拜一下
那一剑的风情
那一剑的风情

引用来自“LiuZiyong”的评论

如果进行概念类比的话,开发一个nginx的模块,是不是类似于开发一个tomcat离得webapp呢?

不熟悉tomcat,开发nginx模块,只是为了丰富nginx本身的功能
L
LiuZiyong
如果进行概念类比的话,开发一个nginx的模块,是不是类似于开发一个tomcat离得webapp呢?
算法与编程之美
算法与编程之美
如果能多一些图例的话,感觉会更好
xeonwell
xeonwell
膜拜一下下
那一剑的风情
那一剑的风情

引用来自“php.by.live”的评论

引用来自“那一剑的风情”的评论

引用来自“php.by.live”的评论

是你转的 还是你写的啊 ?

原创的,亲

请问除了C语言 还要学习哪些知识 就可以熟练看懂这些源码啊??

编程这东东,知识面越广,阅读起来越不费劲。有C基础,就可以阅读,不懂的知识,比如socket编程,http,epoll这种,网上查一大把的,多思想,有耐性就可以了,good luck
纠结名字_我艹你妹
纠结名字_我艹你妹

引用来自“那一剑的风情”的评论

引用来自“php.by.live”的评论

是你转的 还是你写的啊 ?

原创的,亲

请问除了C语言 还要学习哪些知识 就可以熟练看懂这些源码啊??
那一剑的风情
那一剑的风情

引用来自“php.by.live”的评论

是你转的 还是你写的啊 ?

原创的,亲
纠结名字_我艹你妹
纠结名字_我艹你妹
是你转的 还是你写的啊 ?
nginx源码分析——模块

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

hncscwc
2016/06/17
120
0
Nginx 源码分析:从模块到配置(下)

本篇的上篇 Nginx 源码分析:从模块到配置(上),建议阅读本篇前先阅读上篇。 关于模块 的架构高度模块化。每个模块各司其职,组合在一起完成特定的功能。 通过决定哪些模块被安装。所有安装...

_Zhao
08/27
0
0
【推荐】微服务大型分布式企业框架 Springmvc+mybatis+shiro+Dubbo+ZooKeeper+Redis

平台简介 Jeesz是一个分布式的框架,提供项目模块化、服务化、热插拔的思想,高度封装安全性的Java EE快速开发平台。 Jeesz本身集成Dubbo服务管控、Zookeeper注册中心、Redis分布式缓存技术、...

清风徐来l
2017/06/09
365
1
分布式架构真正适用于大型互联网项目的架构! dubbo+zookeeper+springmvc+mybatis+shiro+redis

平台简介 Jeesz是一个分布式的框架,提供项目模块化、服务化、热插拔的思想,高度封装安全性的Java EE快速开发平台。 Jeesz本身集成Dubbo服务管控、Zookeeper注册中心、Redis分布式缓存技术、...

hello你好abc
2017/04/25
1K
3
【推荐】微服务大型分布式企业框架 Springmvc+mybatis+shiro+Dubbo+ZooKeeper+Redis+KafKa

平台简介 Jeesz是一个分布式的框架,提供项目模块化、服务化、热插拔的思想,高度封装安全性的Java EE快速开发平台。 Jeesz本身集成Dubbo服务管控、Zookeeper注册中心、Redis分布式缓存技术、...

码上来l
2017/06/05
644
7

没有更多内容

加载失败,请刷新页面

加载更多

小白带你认识netty(三)之NioEventLoop的线程(或者reactor线程)启动(一)

在第一章中,我们看关于NioEventLoopGroup的初始化,我们知道了NioEventLoopGroup对象中有一组EventLoop数组,并且数组中的每个EventLoop对象都对应一个线程FastThreadLocalThread,那么这个...

天空小小
今天
2
0
PHP动态扩展Redis模块

查看已有模块 [root@test-a ~]# /usr/local/php/bin/php -m[PHP Modules]bz2Core...zlib[Zend Modules] 下载包,解压,生成configure文件 [root@test-a ~]# cd /usr/local/src/[ro......

野雪球
今天
2
0
在Ignite中使用线性回归算法

在本系列前面的文章中,简单介绍了一下Ignite的机器学习网格,下面会趁热打铁,结合一些示例,深入介绍Ignite支持的一些机器学习算法。 如果要找合适的数据集,会发现可用的有很多,但是对于...

李玉珏
今天
3
0
Mybatis应用学习——简单使用示例

1. 传统JDBC程序中存在的问题 1. 一个简单的JDBC程序示例: public class JDBCDemo {public static void main(String[] args) {Connection con=null;PreparedStatement statemen...

江左煤郎
今天
4
0
使用JavaScript编写iOS应用业务逻辑

JSAUIKitCocoa使你可以使用JavaScript编写对性能要求不高但可能变动性很大的iOS应用的业务逻辑部分,View组件、需要多线程支持的Model等则直接使用原生对象。 编写方式与React Native相似,但...

neal01
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部