以前虽然用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;
};