一、创建扩展模块目录

我们在 nginx 文件夹下,创建一个/extends/ngx_http_hello_module的目录

本机的路径:

/usr/local/webserver/nginx/extends/ngx_http_hello_module

二、创建config文件

我们的模块名称为:ngx_http_hello_module

我们需要在 /usr/local/webserver/nginx/extends/ngx_http_hello_module 创建一个config文件,这样Nginx在编译的时候,会将你自定义的模块编译进去

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"

三、创建ngx_http_hello_module.c文件

创建自定义的模块必须遵守Nginx的模块规范,需要定义:

ngx_command_t      命令行解析结构
ngx_http_module_t  模块的上下文结构
ngx_module_t       模块结构

如果对这几个数据结构还不熟悉,可以去Nginx官网学习。

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
 
static ngx_int_t ngx_http_hello_handler(ngx_http_request_t *r);
 
static char *
ngx_http_hello(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
/**
 * 处理nginx.conf中的配置命令解析
 * 例如:
 * location /hello {
 *  	hello
 * }
 * 当用户请求:http://127.0.0.1/hello的时候,请求会跳转到hello这个配置上
 * hello的命令行解析回调函数:ngx_http_hello
 */
static ngx_command_t ngx_http_hello_commands[] = {
	{
		ngx_string("hello"),           //配置项名称
		NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,    
		ngx_http_hello,                //set 函数 
		NGX_HTTP_LOC_CONF_OFFSET,      //偏移  
		0,
		NULL
	},
	ngx_null_command
};
 
/**
 * ngx_http_module_t接口,模块上下文
 */
 //如果没有什么工作是必须在HTTP框架初始化时完成,则不必实现8个回调
static ngx_http_module_t ngx_http_hello_module_ctx = { NULL, NULL, NULL, 
    NULL,NULL, NULL, NULL, NULL };
		
/**
 * 模块的定义
 */
ngx_module_t ngx_http_hello_module = {
	NGX_MODULE_V1,
	&ngx_http_hello_module_ctx,
	ngx_http_hello_commands,
	NGX_HTTP_MODULE,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NGX_MODULE_V1_PADDING
};
 
/**
 * 命令解析的回调函数
 * 该函数中,主要获取loc的配置,并且设置location中的回调函数handler
 */
static char *
ngx_http_hello(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
	ngx_http_core_loc_conf_t *clcf;
    //找到 hello 所属配置块
	clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
	/* 设置回调函数。当请求http://127.0.0.1/hello的时候,会调用此回调函数 */
	//主机域名、URI与mytest所在配置块匹配时,将调用handler
	clcf->handler = ngx_http_hello_handler;
 
	return NGX_CONF_OK;
}
 
/**
 * 模块回调函数,输出hello world,处理实际用户请求
 */
static ngx_int_t ngx_http_hello_handler(ngx_http_request_t *r) {
    //请求方法必须是GET或者HEAD,否则返回405 \NOT ALLOWED
	if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
		return NGX_HTTP_NOT_ALLOWED;
	}
    //丢弃请求中的包体
	ngx_int_t rc = ngx_http_discard_request_body(r);
	if (rc != NGX_OK) {
		return rc;
	}
 
    //设置返回的Content-Type ngx_string是一个宏可以初始化data字段和len字段
	ngx_str_t type = ngx_string("text/plain");
	ngx_str_t response = ngx_string("Hello World");
	//响应包体内容和状态码设置
	r->headers_out.status = NGX_HTTP_OK;
	r->headers_out.content_length_n = response.len;
	r->headers_out.content_type = type;
    
    //发送http头部
	rc = ngx_http_send_header(r);
	if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
		return rc;
	}
 
    //构造ngx_buf_t结构体准备发送报文
	ngx_buf_t *b;
	b = ngx_create_temp_buf(r->pool, response.len);
	if (b == NULL) {
		return NGX_HTTP_INTERNAL_SERVER_ERROR;
	}
   
    //拷贝响应报文
	ngx_memcpy(b->pos, response.data, response.len);
	b->last = b->pos + response.len;
	//声明这是最后一块缓冲区
	b->last_buf = 1;
   
    //构造发送时的ngx_chain_t结构体
	ngx_chain_t out;
	out.buf = b;
	out.next = NULL;
  
    //发送响应,结束后HTTP框架会调用ngx_http_finalize_request方法结束请求
    return ngx_http_output_filter(r, &out);
}

最终如图所示:

nginx 自定义错误页面失效 nginx自定义模块编写_中间件

四、修改Nginx.conf文件

我们需要通过 http://127.0.0.1/hello 或者 http://localhost/hello 就能访问到我们自定义的hello模块。所以我们需要修改一下Nginx.conf文件:

#ngx_http_hello_module.c
location /hello {
	hello;
}

修改后的 nginx.conf 配置文件:

#运行用户
#user  nobody;
#启动进程,通常设置成和cpu的数量相等
worker_processes  1;

#全局错误日志及PID文件
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

#工作模式及连接数上限
events {
    #单个后台worker process进程的最大并发链接数
    worker_connections  1024; 
}

#设定http服务器,利用它的反向代理功能提供负载均衡支持
http {
    #设定mime类型,类型由mime.type文件定义
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
    
    #设定日志格式
    #access_log  logs/access.log  main;

    #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,对于普通应用,
    #必须设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime.
    sendfile        on;
    #tcp_nopush     on;

    #连接超时时间
    #keepalive_timeout  0;
    keepalive_timeout  65;
    
    #开启gzip压缩
    #gzip  on;

    server {
        #侦听80端口
        listen       80;
        #定义使用localhost访问
        server_name  localhost;

        #charset koi8-r;
        #access_log  logs/host.access.log  main;

        #默认请求
        location / {
            #定义服务器的默认网站根目录位置
            root   html;
            index  index.html index.htm index.php;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #定义错误提示页面
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        # PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置.
        location ~ \.php$ {
            root           html;
            #fastcgi_pass unix:/tmp/php-cgi.sock;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }

        ###########添加部分######################## 
        # ngx_http_hello_module.c 新加入的模块
        location /hello {
            hello;
        }
        ##########################################


        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        # 禁止访问 .htxxx 文件
        #location ~ /\.ht {
        #    deny  all;
        #}
    }

    # another virtual host using mix of IP-, name-, and port-based configuration
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

    # HTTPS server
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;
    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;
    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;
    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    
}

五、编译Nginx源码

编译自定义的模块,需要加上:--add_module=/模块文件夹,即./configure --prefix=/nginx的文件路径 --add-module=/module的文件路径

注意:编译前确认 /usr/local/webserver/nginx目录下有configure文件,否则会报错bash: ./configure: 没有那个文件或目录

nginx 自定义错误页面失效 nginx自定义模块编写_职场和发展_02

sudo ./configure --prefix=/usr/local/webserver/nginx --add-module=/usr/local/webserver/nginx/extends/ngx_http_hello_module

sudo make && make install

编译后,生成模块数组文件:objs/ngx_modules.c 里面就包含我们自定义的模块

小技巧:可以用/usr/local/webserver/nginx/sbin/nginx -V查看路径是否加载了ngx_http_hello_module(使用nginx -V命令可以查看我们安装的nginx版本)

nginx 自定义错误页面失效 nginx自定义模块编写_中间件_03

模块数组*ngx_modules[] 中的值就是通过这个文件初始化的,然后通过这个模块数组对每个模块进行初始化

extern ngx_module_t  ngx_http_limit_req_module;
extern ngx_module_t  ngx_http_geo_module;
extern ngx_module_t  ngx_http_map_module;
extern ngx_module_t  ngx_http_split_clients_module;
extern ngx_module_t  ngx_http_referer_module;
extern ngx_module_t  ngx_http_rewrite_module;
extern ngx_module_t  ngx_http_proxy_module;
extern ngx_module_t  ngx_http_fastcgi_module;
extern ngx_module_t  ngx_http_uwsgi_module;
extern ngx_module_t  ngx_http_scgi_module;
extern ngx_module_t  ngx_http_memcached_module;
extern ngx_module_t  ngx_http_empty_gif_module;
extern ngx_module_t  ngx_http_browser_module;
extern ngx_module_t  ngx_http_upstream_hash_module;
extern ngx_module_t  ngx_http_upstream_ip_hash_module;
extern ngx_module_t  ngx_http_upstream_least_conn_module;
extern ngx_module_t  ngx_http_upstream_keepalive_module;
extern ngx_module_t  ngx_http_upstream_zone_module;
extern ngx_module_t  ngx_http_hello_module;                 //新增的hello_module
extern ngx_module_t  ngx_http_write_filter_module;
extern ngx_module_t  ngx_http_header_filter_module;
extern ngx_module_t  ngx_http_chunked_filter_module;
extern ngx_module_t  ngx_http_range_header_filter_module;
extern ngx_module_t  ngx_http_gzip_filter_module;
extern ngx_module_t  ngx_http_postpone_filter_module;
extern ngx_module_t  ngx_http_ssi_filter_module;
extern ngx_module_t  ngx_http_charset_filter_module;
extern ngx_module_t  ngx_http_userid_filter_module;
extern ngx_module_t  ngx_http_headers_filter_module;
extern ngx_module_t  ngx_http_copy_filter_module;
extern ngx_module_t  ngx_http_range_body_filter_module;
extern ngx_module_t  ngx_http_not_modified_filter_module;

六、浏览器访问

在浏览器中输入http://127.0.0.1/hello 就能访问我们自定义的hello模块了。

nginx 自定义错误页面失效 nginx自定义模块编写_linux_04