文档章节

nginx源码分析之模块化

那一剑的风情
 那一剑的风情
发布于 2012/09/20 10:21
字数 1213
阅读 3553
收藏 43
点赞 1
评论 10

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

 


© 著作权归作者所有

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

那一剑的风情

粉丝 119
博文 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源码分析—启动流程

作者:阿波 本文链接:http://blog.csdn.net/livelylittlefish/article/details/7243718 Content 0. 序 1. main()分析 2. 注意问题 2.1 几个初值 2.2 nginx工作模式 2.3 一些配置 2.4 其他开...

晨曦之光
2012/03/09
1K
0
初识Nginx——nginx的编译、安装及特点(一)

一、Nginx简介 nginx是一个轻量级的服务器软件,目前世界排名第三,第一占据大部分的市场份额的是apache,第二的是微软公司的IIS站的比重大约是23%。nginx自开发出来04年公布出来市场份额逐年...

190840971
2014/12/28
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
Nginx模块开发指南:使用C++11和Boost程序库

【下载地址】 Nginx 是由俄罗斯工程师Igor Sysoev 开发的一个高性能Web 服务器,运行效率远超传统的Apache、Tomcat,是世界第二大Web 服务器,被国内外诸多顶级互联网公司采用。 Nginx 的一个...

winter730
05/25
0
0
分布式架构真正适用于大型互联网项目的架构! dubbo+zookeeper+springmvc+mybatis+shiro+redis

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

qq59140ea121ae2
2017/05/11
0
0
好文分享--java企业架构 spring mvc +mybatis + KafKa+Flume+Zookeeper分布式架构

开发工具 1.Eclipse IDE:采用Maven项目管理,模块化。 2.代码生成:通过界面方式简单配置,自动生成相应代码,目前包括三种生成方式(增删改查):单表、一对多、树结构。生成后的代码如果不...

崛起的小金牛
2017/06/16
684
6
【推荐】微服务大型分布式企业框架 Springmvc+mybatis+shiro+Dubbo+ZooKeeper+Redis+KafKa

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

码上来l
2017/06/05
644
7
精华【分布式、微服务、云架构、dubbo+zookeeper+springmvc+mybatis+shiro+redis】分布式大型互联网企业架构!

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

开心快le每一天
2017/05/26
206
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

SpringBoot | 第十章:Swagger2的集成和使用

前言 前一章节介绍了mybatisPlus的集成和简单使用,本章节开始接着上一章节的用户表,进行Swagger2的集成。现在都奉行前后端分离开发和微服务大行其道,分微服务及前后端分离后,前后端开发的...

oKong
今天
4
0
Python 最小二乘法 拟合 二次曲线

Python 二次拟合 随机生成数据,并且加上噪声干扰 构造需要拟合的函数形式,使用最小二乘法进行拟合 输出拟合后的参数 将拟合后的函数与原始数据绘图后进行对比 import numpy as npimport...

阿豪boy
今天
1
0
云拿 无人便利店

附近(上海市-航南路)开了家无人便利店.特意进去体验了一下.下面把自己看到的跟大家分享下. 经得现场工作人员同意后拍了几张照片.从外面看是这样.店门口的指导里强调:不要一次扫码多个人进入....

周翔
昨天
1
0
Java设计模式学习之工厂模式

在Java(或者叫做面向对象语言)的世界中,工厂模式被广泛应用于项目中,也许你并没有听说过,不过也许你已经在使用了。 简单来说,工厂模式的出现源于增加程序序的可扩展性,降低耦合度。之...

路小磊
昨天
165
1
npm profile 新功能介绍

转载地址 npm profile 新功能介绍 npm新版本新推来一个功能,npm profile,这个可以更改自己简介信息的命令,以后可以不用去登录网站来修改自己的简介了 具体的这个功能的支持大概是在6这个版...

durban
昨天
1
0
Serial2Ethernet Bi-redirection

Serial Tool Serial Tool is a utility for developing serial communications, custom protocols or device testing. You can set up bytes to send accordingly to your protocol and save......

zungyiu
昨天
1
0
python里求解物理学上的双弹簧质能系统

物理的模型如下: 在这个系统里有两个物体,它们的质量分别是m1和m2,被两个弹簧连接在一起,伸缩系统为k1和k2,左端固定。假定没有外力时,两个弹簧的长度为L1和L2。 由于两物体有重力,那么...

wangxuwei
昨天
0
0
apolloxlua 介绍

##项目介绍 apolloxlua 目前支持javascript到lua的翻译。可以在openresty和luajit里使用。这个工具分为两种模式, 一种是web模式,可以通过网页使用。另外一种是tool模式, 通常作为大规模翻...

钟元OSS
昨天
2
0
Mybatis入门

简介: 定义:Mybatis是一个支持普通SQL查询、存储过程和高级映射的持久层框架。 途径:MyBatis通过XML文件或者注解的形式配置映射,实现数据库查询。 特性:动态SQL语句。 文件结构:Mybat...

霍淇滨
昨天
2
0
开发技术瓶颈期,如何突破

前言 读书、学习的那些事情,以前我也陆续叨叨了不少,但总觉得 “学习方法” 就是一个永远在路上的话题。个人的能力、经验积累与习惯方法不尽相同,而且一篇文章甚至一本书都很难将学习方法...

_小迷糊
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部