nginx学习

以前虽然用nginx。不过都是简单的方向代理配置。另外是有需求百度一点点解决。也不管为什么这么写。现在打算系统的学习一下总结一些:

基本配置

user xiezhaojun _appserveradm;
#user  nobody;
worker_processes  1;
error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        #listen address:port[default(deprecated in 0.8.21)|default_server|backlog=num|rcvbuf=size|sndbuf=size|accept_filter=filter|deferred|bind|ipv6only=[on|off]|ssl]]
        #监听端口  以及一些信息 默认是80
        listen 80 ;

        #监听的host 优先全匹配  其次 *.com  这样的前通配 再其次  xzjtext.* 这样的 后通配。 最后 匹配~^\.testweb\.com$ 正则. 
        #如果 都匹配不了 就找上面 listen有[default|default_server]的服务  
        #还没有 就第一个了
        server_name xzjtest.com;

        #每个散列桶占用的内存大小。
        #server_names_hash_bucket_size 32|64|128;

        #server_names_hash_max_size越大,消耗的内存就越多,但散列key的冲突率则会降低,检索速度也更快。server_names_hash_max_size越小,消耗的内存就越小,但散列key的冲突率可能增高。
        #server_names_hash_max_size 512;

        #server_name_in_redirect on; 在使用on打开时,表示在重定向请求时会使用server_name里配置的第一个主机名代替原先请求中的Host头部,而使用off关闭时,表示在重定向请求时使用请求本身的Host头部

    #通过rewrite 达到redirect到默认首页的目的。
     if ( $host = 'xzjtest.com'){
         rewrite ^/$ http://xzjtest.com/index ;
     }
    #location 对uri 进一步匹配找到对应的处理 
    # "=" 是表示对uri的全匹配 
    # "~" 表示匹配是大小写敏感的 (可用正则表达式)
    # "~*" 是表示忽略大小写 (可用正则表达式)
    # "^~" 是表示如果能匹配字符串 那么就直接匹配。如果不能就当正则表达式来匹配. 基本就可以当做前缀适用前面的 比如 ^~ images  可以匹配  xzjtest.com/images/bigImage/......
    #  ~* .(gif|jpg|jpeg)$   上面的可以用正则表达式的 但是优先级比不适用表达式低。当多个正则表达式都能匹配 那么就会选择醉长的匹配
    location = /lua {
        #配置读取本地文件的地址
        #root  /Users/xiezhaojun/Desktop/images/1 ;

        #也是读取本地文件 区别是这个路径是抛弃掉 location 上面的解析路径。下面的配置 和上面的配置读取的文件相同。
        alias /Users/xiezhaojun/Desktop/images/1/images ;
    }

    location ~ ^/test/(\w+)\.(\w+)$ {
        #可以利用正则表达式很好的匹配各种情况
        alias /Users/xiezhaojun/Desktop/images/$1/images/$1.$2;
    }

    location {
        #配置首页
        index index.html htmlindex.php /index.php;
        #根据返回码匹配对应的页面 这个虽然重定向了uri 但是状态吗没有改变
        error_page 404 404.html;
        #可以这样改变状态吗
        error_page 404 =200 empty.gif;
        #也可以重定向到内部的location去处理
        error_page 404 @fallback;
    }

    # @开头的location 表示内部使用的 不解析外部的
    location @fallback (
        #反向代理 表示 转发到上游uri来处理这个请求
        proxy_pass http://localhost:8000/uri/ ;

        #这个是表示 使用 upstream块
        proxy_pass http://backend
    }


    location {
        #设置递归调用错误的
        recursive_error_pages off;
        #尝试一个个的获取一直到可以获取就结束。并且返回。最后一个必须是有效的uri
        try_files systemmaintenance.html $uri $uri/index.html $uri.html @fallback;
        #也可以指定错误码 这样就配合page_no 来处理了
        try_files $uri $uri /error.phpc=404 =404;
    }

    #设置上游服务器集群
    upstream backend {
        # weight 表示权重
        server backend1.example.com weight=5;
        #当前的上游服务器转发失败次数超过number,则认为在当前的fail_timeout时间段内这台上游服务器不可用
        #fail_timeout表示该时间段内转发失败多少次后就认为上游服务器暂时不可用,用于优化反向代理功能。
        server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
        server unix:/tmp/backend3;
    }

    upstream backend {
        #ip_hash 使用hash地址的方法 保证一个地址永远是一个上游服务器。如果有上游服务器不可用了。不能直接移除 需要设置成down
        #ip_hash 和 weight 冲突
        ip_hash;
        server backend1.example.com;
        server backend2.example.com;
        server backend3.example.com down;
        server backend4.example.com;
    }

    location /oauth/check{ 
        proxy_pass http://localhost:8080;

    } 
    error_page 401 http://www.baidu.com;
    error_page 402 http://www.baidu.com;

    }
}

来个配置 上面 基本已经说明了最简单的config文件的东西。
上面漏掉了 rewrite 。就是重写到哪个地址。

自定义模块

首先还是先上代码例子。从hello world做起
ngx_http_mytest_module.c 文件

#include 
#include 
#include 

static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);
static char* ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

//配置项
/*
struct ngx_command_s {
    ngx_str_t name;  //name是配置项名称,
    ngx_uint_t type; //其中,type决定这个配置项可以在哪些块(如http、server、location、if、upstream块等)

    //set 回调方法 如果是带参数的可以用系统x预设的14个解析配置项方法,这会少写许多代码 当然自己写也行
    char (set)(ngx_conf_t cf, ngx_command_t cmd, void conf);

    //conf用于指示配置项所处内存的相对偏移位置,仅在type中没有设置NGX_DIRECT_CONF和NGX_MAIN_CONF时才会生效。对于HTTP模块,conf是必须要设置的,它的取值范围
    //NGX_HTTP_MAIN_CONF_OFFSET 使用 create_main_conf 方法产生的结构体来保存解析出来的参数
    //NGX_HTTP_SRV_CONF_OFFSET  使用 create_srv_conf 方法产生的结构体来保存解析出来的参数
    //NGX_HTTP_LOC_CONF_OFFSET  使用 create_loc_conf 方法产生的结构体来保存解析出来的参数
    ngx_uint_t conf;

    //如果是自己解析的这个参数并不重要。自己处理好逻辑就行。如果用的是系统预设的14种处理需要设置让系统知道设置到结构体的哪里
    //offsetof(ngx_http_mytest_conf_t, my_str) 这个就是 设置系统预设的ngx_conf_set_str_slot 的例子
    ngx_uint_t offset;
    void post;
};
*/
static ngx_command_t ngx_http_mytest_commands[] = {
    {   
        ngx_string("mytest"),//指令名称,在配置文件中使用
        NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
        ngx_http_mytest, // 回调函数
        NGX_HTTP_LOC_CONF_OFFSET, //位置
        0, //指令的值保存的位置
        NULL
    },
    ngx_null_command
};
//模块上下文
/*
typedef struct {
    ngx_int_t (*preconfiguration)(ngx_conf_t cf);
    ngx_int_t (postconfiguration)(ngx_conf_t cf);
    void (*create_main_conf)(ngx_conf_t cf);
    char (*init_main_conf)(ngx_conf_t cf, void conf);
    void (create_srv_conf)(ngx_conf_t cf);
    char (*merge_srv_conf)(ngx_conf_t cf, void prev, void *conf);
    void (create_loc_conf)(ngx_conf_t cf);
    char (*merge_loc_conf)(ngx_conf_t cf, void prev, void *conf);
} ngx_http_module_t;
*/
static ngx_http_module_t ngx_http_mytest_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};

//新的模块
/*
typedef struct ngx_module_s ngx_module_t; 
struct ngx_module_s {

}
*/
ngx_module_t ngx_http_mytest_module = {
    NGX_MODULE_V1,
    &ngx_http_mytest_module_ctx,
    ngx_http_mytest_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};
//配置回调函数
static char* ngx_http_mytest(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_mytest_handler;
    return NGX_CONF_OK;
}

//回调函数 handler
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
{
    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;

    ngx_str_t type = ngx_string("text/plain");
    ngx_str_t response = ngx_string("Hello, this is nginx world!");
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = response.len;
    r->headers_out.content_type = type;

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

    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 out; //构造输出链表
    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}

config 文件

ngx_addon_name=ngx_http_mytest_module
HTTP_MODULES="$HTTP_MODULES ngx_http_mytest_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest_module.c"

在编译的时候 –add-module= path 是上面两个文件放置的文件夹。这样就将自定义module加入到的nginx里面去了。
这样 在nginx.conf 文件中 加入“mytest;” 配置就会返回 Hello, this is nginx world!

我们先来看一下 ngx_module_t 这个结构体的定义。
下面是截取的《深入理解nginx》一书的对ngx_module_t这个结构体的解释

typedef struct ngx_module_s ngx_module_t;
 struct ngx_module_s {
    /* 下面的ctx_index、index、spare0、spare1、spare2、spare3、version变量不需要在定义时赋值,可以用Nginx准备好的宏NGX_MODULE_V1来定义,它已经定义好了这7个值。
#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1
对于一类模块(由下面的type成员决定类别)而言,ctx_index表示当前模块在这类模块中的序号。这个成员常常是由管理这类模块的一个
Nginx核心模块设置的,对于所有的HTTP模块而言,ctx_index是由核心模块ngx_http_module设置的。
ctx_index非常重要,Nginx的模块化设计非常依赖于各个模块的顺序,它们既用于表达优先级,也用于表明每个模块的位置,借以帮助Nginx框架快速获得某个模块的数据(
HTTP框架设置
ctx_index的过程参见
10.7节)
*/
ngx_uint_t ctx_index;
/*index表示当前模块在ngx_modules数组中的序号。注意,ctx_index表示的是当前模块在一类模块中的序号,而index表示当前模块在所有模块中的序号,
它同样关键。Nginx启动时会根据ngx_modules数组设置各模块的index值。例如:
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++; }
*/
ngx_uint_t index;
// spare系列的保留变量,暂未使用
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t spare2;
ngx_uint_t spare3;
// 模块的版本,便于将来的扩展。目前只有一种,默认为1
ngx_uint_t version;
/*ctx用于指向一类模块的上下文结构体,为什么需要ctx呢?因为前面说过,Nginx模块有许多种类,不同类模块之间的功能差别很大。
例如,事件类型的模块主要处理I/O事件相关的功能,
HTTP类型的模块主要处理HTTP应用层的功能。
这样,每个模块都有了自己的特性,而ctx将会指向特定类型模块的公共接口。
例如,在HTTP模块中,ctx需要指向ngx_http_module_t结构体
*/
void *ctx;
// commands将处理nginx.conf中的配置项,详见第4章
ngx_command_t *commands;
/*type表示该模块的类型,它与ctx指针是紧密相关的。
在官方Nginx中,它的取值范围是以下
5种:
NGX_HTTP_MODULE、
NGX_CORE_MODULE、
NGX_CONF_MODULE、
NGX_EVENT_MODULE、
NGX_MAIL_MODULE。这
5种模块间的关系参考图
8-2。实际上,还可以自定义新的模块类型
*/
ngx_uint_t type;
/*在
Nginx的启动、停止过程中,以下
7个函数指针表示有
7个执行点会分别调用这
7种方法(参见
8.4节~
8.6节)。对于任一个方法而言,如果不需要
Nginx在某个时刻执行它,那么简单地把它设为
NULL空指针即可
*/
/*虽然从字面上理解应当在
master进程启动时回调
init_master,但到目前为止,框架代码从来不会调用它,因此,可将
init_master设为
NULL */
ngx_int_t (*init_master)(ngx_log_t *log); /*init_module回调方法在初始化所有模块时被调用。在
master/worker模式下,这个阶段将在启动
worker子进程前完成
*/
ngx_int_t (*init_module)(ngx_cycle_t *cycle); /* init_process回调方法在正常服务前被调用。在
master/worker模式下,多个
worker子进程已经产生,在每个
worker进程的初始化过程会调用所有模块的
init_process函数
*/
ngx_int_t (*init_process)(ngx_cycle_t *cycle); /* 由于
Nginx暂不支持多线程模式,所以
init_thread在框架代码中没有被调用过,设为
NULL*/
ngx_int_t (*init_thread)(ngx_cycle_t *cycle); // 同上,
exit_thread也不支持,设为
NULL
void (*exit_thread)(ngx_cycle_t *cycle); /* exit_process回调方法在服务停止前调用。在
master/worker模式下,
worker进程会在退出前调用它
*/
void (*exit_process)(ngx_cycle_t *cycle); 
// exit_master回调方法将在master进程退出前被调用
void (*exit_master)(ngx_cycle_t *cycle); 
/*以下
8个
spare_hook变量也是保留字段,目前没有使用,但可用
Nginx提供的
NGX_MODULE_V1_PADDING宏来填充。看一下该宏的定义:
#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0*/
uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};

我们主要实在处理http 模块。所以type 基本是NGX_HTTP_MODULE。重要的东西就剩下2个:
void *ctx;
ngx_command_t *commands;

对于HTTP类型的模块来说,
ngx_module_t中的ctx指针必须指向ngx_http_module_t接口(HTTP框架的要求)

typedef struct {
    // 解析配置文件前调用
    ngx_int_t (*preconfiguration)(ngx_conf_t cf);
    //完成配置文件解析后调用


    ngx_int_t (postconfiguration)(ngx_conf_t cf);

    //当需要创建数据结构用于存储main级别(直属于http{...}块的配置项)的全局配置项时,可以通过create_main_conf回调方法创建存储全局配置项的结构体
    void (*create_main_conf)(ngx_conf_t cf);
     常用于初始化main级别配置项
    char (*init_main_conf)(ngx_conf_t cf, void conf);

    //当需要创建数据结构用于存储srv级别(直属于虚拟主机server{...}块的配置项)的配置项时,可以通过实现create_srv_conf回调方法创建存储srv级别配置项的结构体
    void (create_srv_conf)(ngx_conf_t cf);

     merge_srv_conf回调方法主要用于合并main级别和srv级别下的同名配置项
    char (*merge_srv_conf)(ngx_conf_t cf, void prev, void *conf);

    ///*当需要创建数据结构用于存储loc级别(直属于location{...}块的配置项)的配置项时,可以实现create_loc_conf回调方法
    void (create_loc_conf)(ngx_conf_t cf);

     merge_loc_conf回调方法主要用于合并srv级别和loc级别下的同名配置项
    char (*merge_loc_conf)(ngx_conf_t cf, void prev, void *conf);
} ngx_http_module_t;

commands数组用于定义模块的配置文件参数,每一个数组元素都是ngx_command_t类型,数组的结尾用ngx_null_command表示。
Nginx在解析配置文件中的一个配置项时首先会遍历所有的模块,对于每一个模块而言,即通过遍历commands数组进行,
另外,在数组中检查到ngx_null_command时,会停止使用当前模块解析该配置项。每一个ngx_command_t结构体定义了自己感兴趣的一个配置项:

typedef struct ngx_command_s ngx_command_t; 
struct ngx_command_s {
    // 配置项名称,如"gzip"
    ngx_str_t name;
    /*配置项类型,
    type将指定配置项可以出现的位置。例如,出现在server{}或location{}中,以及它可以携带的参数个数
    */
    ngx_uint_t type;
    // 出现了name中指定的配置项后,将会调用set方法处理配置项的参数
    char *(*set)(ngx_conf_t cf, ngx_command_t cmd, void *conf); 
    // 在配置文件中的偏移量
    ngx_uint_t conf;
    /*通常用于使用预设的解析方法解析配置项,这是配置模块的一个优秀设计。它需要与
    conf配合使用,
    */
    ngx_uint_t offset;
    // 配置项读取后的处理方法,必须是ngx_conf_post_t结构的指针
    void *post;
};

你可能感兴趣的:(服务器)