1.初步认识nginx

1.简介

Nginx是当前最流行的HTTP Server之一,与Apache相比,Nginx在高并发情况下具有巨大的性能优势。

Apache工作方式:每个请求会独占一个工作线程,当并发数上到几千时,就同时有几千的线程在处理请求了。这对操作系统来说,是个不小的挑战,线程带来的内存占用非常大,线程的上下文切换带来的cpu开销很大,自然性能就上不去了,而这些开销完全是没有意义的。

Nginx工作模式:nginx在启动后,会有一个master进程和多个worker进程。master进程主要用来管理worker进程,work进程是用来工作的进程。模型如下图所示:

1.初步认识nginx_第1张图片

master进程会向worker进程发送各种信号,监控worker进程的运行状态,当worker进程异常退出后,会自动重启新的worker进程。

多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程相互之间是独立的。一个请求,只可能放在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。

worker进程的个数是可以设置的,通常会设置于机器cpu核数一致。


Nginx进程模型的好处:首先,独立的进程,不需要加锁,所以省掉了锁带来的开销;其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其他进程还在工作,服务不会中断,master进程很快会启动新的worker进程。


Nginx如何处理高并发:nginx采用异步非阻塞的方式来处理请求,可以同时处理成千上万个请求。具体到系统调用就是像select/poll/epoll/kqueue这样的系统调用。它们提供一种机制,让你可以同时监控多个事件,调用它们是阻塞的,但可以设置超时时间,在超时时间之内,如果有事情准备好了,就返回。以epoll为例,当事件没准备好时,放到epoll里面,事件准备好了,就去读写,当读写返回EAGAIN时,将它再次加入到epoll里面。这样,只要有事件准备好了,我们就去处理它,只有当所有事件都没准备好时,才在epoll里面等着。

这样我们就可以并发处理大量的并发,当然这里的并发请求,是指未处理完的请求,线程只有一个,所以同时能处理的请求只有一个,只是在请求间进行不断切换而已,切换也是因为异步事件为准备好,而主动让出的,这里的切换是没有任何代价的,可以理解为循环处理多个准备好的事件。

epoll只遍历活跃的socket描述符,这是因为在内核实现中,epoll是根据每个fd上面的callback函数实现的。

2.Http模块开发

nginx配置文件,层级分布结构如下图所示:
1.初步认识nginx_第2张图片
在开发Nginx HTTP扩展模块过程中,需要特别注意的是main、server和location三个层级,因为扩展模块通常允许指定新的配置指令在这三个层级中。一般来说,一个server block表示一个Host,而里面的一个location则代表一个路由映射规则,这两个block可以说是HTTP配置的核心。
Nginx本身支持多种模块,如HTTP模块、EVENT模块和MAIL模块,通常只讨论HTTP模块。
Nginx本身做的工作实际很少,当它接到一个HTTP请求时,它仅仅是通过查找配置文件将此次请求映射到一个location block,而此location中所配置的各个指令则会启动不同的模块去完成工作,因此模块可以看做Nginx真正的劳动工作者。通常一个location中的指令会涉及一个handler模块和多个filter模块(当然,多个location可以复用同一个模块)。Handler模块负责处理请求,完成响应内容的生成,而filter模块对响应内容进行处理。因此Nginx模块开发分为handler开发和filter开发。下图展示一次常规请求和响应的过程。
1.初步认识nginx_第3张图片


Nginx Http模块开发实战:一个简单完整的http模块代码如下所示。
我们开发了一个叫echo的handler模块,这个模块的功能非常简单,它接收“echo”指令,指令可指定一个字符串参数,模块会输出这个字符串作为HTTP相应。配置如下:
 location /echo{
           echo "hello world!!!!";
         } 
要实现这个功能需要三步:1、读入配置文件中echo指令及其参数;2、进行HTTP包装(添加HTTP头等工作);3、将结果返回给客户端。

定义模块配置结构
首先需要一个结构用于存储从配置文件中读进来的相关指令参数,即模块配置信息结构。 根据Nginx模块开发规则,这个结构的命名规则为ngx_http_[module-name]_[main|srv|loc]_conf_t。其中main、srv和loc分别表示同一模块在三层block中的配置信息。这里的echo模块只需要运行在loc层级下,需要存储一个字符串参数,因此我们可以定义如下的模块配置:
typedef struct{
	ngx_str_t ed;
}ngx_http_echo_loc_conf_t;

其中字段ed用于存储echo指令指定需要输出的字符串。
定义指令
一个Nginx模块往往接受一到多个指令,echo模块接收一个指令“echo”,Nginx模块使用一个ngx_command_t数组表示模块所能接收的所有指令,其中每个元素表示一条指令。ngx_command_t结构如下:
struct ngx_command_s {
    ngx_str_t             name;
    ngx_uint_t            type;
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    ngx_uint_t            conf;
    ngx_uint_t            offset;
    void                 *post;
};
其中name是词条指令的名称,type使用掩码标志位方式配置指令参数。set是一个函数指针,用于指定一个参数转化函数,这个函数一般是将配置文件中相关指令的参数转化成需要的格式并存入配置结构体。Nginx预定义了一些转换函数,可以方便我们调用。这些函数一般以"_slot"结尾,例如ngx_conf_set_flag_slot将"on"或"off"转为1或0,ngx_conf_set_str_slot将裸字符串转化为ngx_str_t。
conf用于指定Nginx相应配置文件内存的真实地址,一般可以通过内存常量指定,如NGX_HTTP_LOC_CONF_OFFSET,offset指定此条指令的参数的偏移量。下面是echo模块的指令定义
static ngx_command_t ngx_http_echo_commands[] = {
	{
		ngx_string("echo"),
		NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
		ngx_http_echo,
		NGX_HTTP_LOC_CONF_OFFSET,
		offsetof(ngx_http_echo_loc_conf_t, ed),
		NULL
	},
	ngx_null_command
};
参数转化函数的代码为:
static char * ngx_http_echo(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_echo_handler;
    ngx_conf_set_str_slot(cf, cmd, conf);
    return NGX_CONF_OK;
}
这个函数除了调用ngx_conf_set_str_slot转化为echo指令的参数外,还将修改了核心模块配置(也就是这个location的配置),将其handler替换为我们编写的ngx_http_echo_handler。这样就屏蔽了此location的默认handler,使用ngx_http_echo_handler产生HTTP响应。
创建合并配置信息
这一步是定义模块Context。这里首先需要定义一个ngx_http_module_t类型的结构体变量,命名规则为ngx_http_[module-name]_module_ctx,这个结构主要用于定义各个Hook函数。下面是echo模块的context结构:
static ngx_http_module_t  ngx_http_echo_module_ctx = {
    NULL,                                  /* preconfiguration */
    NULL,                                  /* postconfiguration */
    NULL,                                  /* create main configuration */
    NULL,                                  /* init main configuration */
    NULL,                                  /* create server configuration */
    NULL,                                  /* merge server configuration */
    ngx_http_echo_create_loc_conf,         /* create location configration */
    ngx_http_echo_merge_loc_conf           /* merge location configration */
};
可以看到一共有8个Hook注入点,分别会在不同的时刻被Nginx调用,由于我们的模块仅仅用于location域,这里将不需要注入点设为NULL即可。 其中create_loc_conf用于初始化一个配置体,如为配置结构分配内存等工作;merge_loc_conf用于将其父block的配置信息合并到此结构体中,也就是实现配置的继承。这两个函数会被Nginx自动调用。这里的命名规则:ngx_http_[module-name]_[create|merge]_[main|srv|loc]_conf。下面分别是这两个函数的代码:
static void *
ngx_http_echo_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_echo_loc_conf_t  *conf;
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
    if (conf == NULL) {
        return NGX_CONF_ERROR;
    }
    conf->ed.len = 0;
    conf->ed.data = NULL;
    return conf;
}
static char *
ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_echo_loc_conf_t *prev = parent;
    ngx_http_echo_loc_conf_t *conf = child;
    ngx_conf_merge_str_value(conf->ed, prev->ed, "");
    return NGX_CONF_OK;
}
其中ngx_pcalloc用于在Nginx内存池中分配一块空间,是pcalloc的一个包装。使用ngx_pcalloc分配的内存空间不必手工free,Nginx会自行管理,在适当时候释放。
create_loc_conf新建一个ngx_http_echo_loc_conf_t,分配内存,并初始化其中的数据,然后返回这个结构的指针;merge_loc_conf将父block域的配置信息合并到create_loc_conf新建的配置结构体中。
其中ngx_conf_merge_str_value不是一个函数,而是一个宏,如下:
#define ngx_conf_merge_str_value(conf, prev, default)                        \
    if (conf.data == NULL) {                                                 \
        if (prev.data) {                                                     \
            conf.len = prev.len;                                             \
            conf.data = prev.data;                                           \
        } else {                                                             \
            conf.len = sizeof(default) - 1;                                  \
            conf.data = (u_char *) default;                                  \
        }                                                                    \
    }
可以看到,core/ngx_conf_file.h还定义了很多merge value的宏用于merge各种数据。它们的行为比较相似:使用prev填充conf,如果prev的数据为空则使用default填充。
编写Handler
handler是模块中真正干活的代码,它主要有以下四项职责:读入模块配置、处理功能业务、产生HTTP header、产生HTTP body。下面是echo模块的代码:
static ngx_int_t
ngx_http_echo_handler(ngx_http_request_t *r)
{
    ngx_int_t rc;
    ngx_buf_t *b;
    ngx_chain_t out;
    ngx_http_echo_loc_conf_t *elcf;
    elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
    if(!(r->method & (NGX_HTTP_HEAD|NGX_HTTP_GET|NGX_HTTP_POST)))
    {
        return NGX_HTTP_NOT_ALLOWED;
    }
    r->headers_out.content_type.len = sizeof("text/html") - 1;
    r->headers_out.content_type.data = (u_char *) "text/html";
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = elcf->ed.len;
    if(r->method == NGX_HTTP_HEAD)
    {
        rc = ngx_http_send_header(r);
        if(rc != NGX_OK)
        {
            return rc;
        }
    }
    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    if(b == NULL)
    {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    out.buf = b;
    out.next = NULL;
    b->pos = elcf->ed.data;
    b->last = elcf->ed.data + (elcf->ed.len);
    b->memory = 1;
    b->last_buf = 1;
    rc = ngx_http_send_header(r);
    if(rc != NGX_OK)
    {
        return rc;
    }
    return ngx_http_output_filter(r, &out);
}
handler会接收一个ngx_http_request_t指针类型的参数,这个参数指向一个ngx_http_request_t结构体,此结构体存储了这次HTTP请求的一些信息,定义如下:
struct ngx_http_request_s {
    uint32_t                          signature;         /* "HTTP" */
    ngx_connection_t                 *connection;
    void                            **ctx;
    void                            **main_conf;
    void                            **srv_conf;
    void                            **loc_conf;
    ngx_http_event_handler_pt         read_event_handler;
    ngx_http_event_handler_pt         write_event_handler;
#if (NGX_HTTP_CACHE)
    ngx_http_cache_t                 *cache;
#endif
    ngx_http_upstream_t              *upstream;
    ngx_array_t                      *upstream_states;
    /* of ngx_http_upstream_state_t */
    ngx_pool_t                       *pool;
    ngx_buf_t                        *header_in;
    ngx_http_headers_in_t             headers_in;
    ngx_http_headers_out_t            headers_out;
    ngx_http_request_body_t          *request_body;
    time_t                            lingering_time;
    time_t                            start_sec;
    ngx_msec_t                        start_msec;
    ngx_uint_t                        method;
    ngx_uint_t                        http_version;
    ngx_str_t                         request_line;
    ngx_str_t                         uri;
    ngx_str_t                         args;
    ngx_str_t                         exten;
    ngx_str_t                         unparsed_uri;
    ngx_str_t                         method_name;
    ngx_str_t                         http_protocol;
    ngx_chain_t                      *out;
    ngx_http_request_t               *main;
    ngx_http_request_t               *parent;
    ngx_http_postponed_request_t     *postponed;
    ngx_http_post_subrequest_t       *post_subrequest;
    ngx_http_posted_request_t        *posted_requests;
    ngx_http_virtual_names_t         *virtual_names;
    ngx_int_t                         phase_handler;
    ngx_http_handler_pt               content_handler;
    ngx_uint_t                        access_code;
    ngx_http_variable_value_t        *variables;
    /* ... */
}
ngx_http_request_s定义比较长,里面有诸如uri,args和request_body等HTTP常用信息。这里需要特别注意的几个字段是headers_in、headers_out和chain,它们分别表示request header、response header和输出缓冲区链表(缓冲区链表是Nginx I/O中的重要内容)。
第一步是获取模块配置信息,这一步只要简单使用ngx_http_get_module_loc_conf就可以了。
第二步是功能逻辑,因为echo模块非常简单,只是简单输出一个字符串,所以这里没有功能逻辑代码。
第三步是设置reponse header。Header内容可以通过填充headers_out实现,这里只设置了Content-type和Content-length等基本内容,ngx_http_headers_out_t定义了所有可以配置的HTTP Response Header 的信息:
typedef struct {
    ngx_list_t                        headers;
    ngx_uint_t                        status;
    ngx_str_t                         status_line;
    ngx_table_elt_t                  *server;
    ngx_table_elt_t                  *date;
    ngx_table_elt_t                  *content_length;
    ngx_table_elt_t                  *content_encoding;
    ngx_table_elt_t                  *location;
    ngx_table_elt_t                  *refresh;
    ngx_table_elt_t                  *last_modified;
    ngx_table_elt_t                  *content_range;
    ngx_table_elt_t                  *accept_ranges;
    ngx_table_elt_t                  *www_authenticate;
    ngx_table_elt_t                  *expires;
    ngx_table_elt_t                  *etag;
    ngx_str_t                        *override_charset;
    size_t                            content_type_len;
    ngx_str_t                         content_type;
    ngx_str_t                         charset;
    u_char                           *content_type_lowcase;
    ngx_uint_t                        content_type_hash;
    ngx_array_t                       cache_control;
    off_t                             content_length_n;
    time_t                            date_time;
    time_t                            last_modified_time;
} ngx_http_headers_out_t;
设置好头信息后使用ngx_http_send_header就可以将头信息输出,ngx_http_send_header接受一个ngx_http_request_t类型的参数。
第四步也是最重要的一步是输出Response body。这里首先需要了解Nginx的I/O机制,Nginx允许handler一次产生一组输出,可以产生多次,Nginx将输出组织成一个单链表结构,链表中的每一个节点是chain_t:
struct ngx_chain_s {
    ngx_buf_t    *buf;
    ngx_chain_t  *next;
};
其中ngx_chain_t是ngx_chain_s的别名,buf为某个数据缓冲区的指针,next指向下一个链表节点。ngx_buf_t中比较重要的是pos和last,分别表示缓冲区数据在内存中的起始地址和结尾地址,last_buf是一个位域,设为1表示此缓冲区是链表中最后一个元素,为0表示后面还有元素。因为我们只有一组数据,所以缓冲区链表中只有一个节点,如果需要输入多组数据可将各组数据放入不同的缓冲区后插入链表。下图展示了Nginx缓冲链表的结构:
1.初步认识nginx_第4张图片
缓冲数据准备好后,用ngx_http_output_filter就可以删除了(会送的filter进行各种过滤处理)。ngx_http_output_filter的第一个参数为ngx_http_request_t结构,第二个为输出链表的起始地址&out。ngx_http_out_put_filter会遍历链表,输出所有数据。
组合Nginx Module
上面完成了Nginx模块各种组件的开发,通过ngx_module_t的结构将各个组件组合起来:
ngx_module_t  ngx_http_echo_module = {
    NGX_MODULE_V1,
    &ngx_http_echo_module_ctx,             /* module context */
    ngx_http_echo_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
};
下面是完成代码
#include 
#include 
#include 

typedef struct{
	ngx_str_t ed;
}ngx_http_echo_loc_conf_t;

static char *ngx_http_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_http_echo_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);

static ngx_command_t ngx_http_echo_commands[] = {
	{
		ngx_string("echo"),
		NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
		ngx_http_echo,
		NGX_HTTP_LOC_CONF_OFFSET,
		offsetof(ngx_http_echo_loc_conf_t, ed),
		NULL
	},
	ngx_null_command
};

static ngx_http_module_t ngx_http_echo_module_ctx = {
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	ngx_http_echo_create_loc_conf,
	ngx_http_echo_merge_loc_conf
};

ngx_module_t ngx_http_echo_module = {
	NGX_MODULE_V1,
	&ngx_http_echo_module_ctx,
	ngx_http_echo_commands,
	NGX_HTTP_MODULE,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NGX_MODULE_V1_PADDING
};

static ngx_int_t ngx_http_echo_handler(ngx_http_request_t *r)
{
	ngx_int_t rc;
	ngx_buf_t *b;
	ngx_chain_t out;
	ngx_http_echo_loc_conf_t *elcf;

	elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
	if (!(r->method & (NGX_HTTP_HEAD|NGX_HTTP_GET|NGX_HTTP_POST)))
	{
		return NGX_HTTP_NOT_ALLOWED;
	}

	r->headers_out.content_type.len = sizeof("text/html")-1;
	r->headers_out.content_type.data = (u_char *)"text/html";
	r->headers_out.status = NGX_HTTP_OK;
	r->headers_out.content_length_n = elcf->ed.len;

	if (r->method == NGX_HTTP_HEAD)
	{
		rc = ngx_http_send_header(r);
		if (rc != NGX_OK)
		{
			return rc;
		}
	}

	b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
	if ( b == NULL )
	{
		ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
		return NGX_HTTP_INTERNAL_SERVER_ERROR;
	}

	out.buf = b;
	out.next = NULL;
	b->pos = elcf->ed.data;
	b->last = elcf->ed.data + (elcf->ed.len);
	b->memory = 1;
	b->last_buf = 1;
	rc = ngx_http_send_header(r);

	if (rc != NGX_OK)
	{
		return rc;
	}

	return ngx_http_output_filter(r, &out);
}

static char * ngx_http_echo(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_echo_handler;
    ngx_conf_set_str_slot(cf, cmd, conf);
    return NGX_CONF_OK;
}

static void * ngx_http_echo_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_echo_loc_conf_t *conf;
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
    if (conf == NULL){
        return NGX_CONF_ERROR;
    }
    conf->ed.len = 0;
    conf->ed.data = NULL;
    return conf;
}

static char * ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_echo_loc_conf_t *prev = parent;
    ngx_http_echo_loc_conf_t *conf = child;

    ngx_conf_merge_str_value(conf->ed, prev->ed, "");
    return NGX_CONF_OK;
}
/* vim: set expandtab sw=4 ts=4 sts=4: */
Nginx模块的安装
Nginx不支持动态链接模块,所以安装模块需要将模块代码与Nginx源码进行重新编译。安装模块的步骤如下:
1.编写config文件,这个文件需要放在和源代码文件在同一个目录下,文件内容如下:
ngx_addon_name=ngx_http_echo_module
HTTP_MODULES="$HTTP_MODULES ngx_http_echo_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_echo_module.c"
2.进入Nginx源代码,使用下面的命令安装编译
./configure --prefix=/home/wangzhilong/mysoft/ --add-module=./myfile/ngx_http_echo/ --with-pcre=../pcre-8.32
make
make install
其中--prefix使用绝对路径,如果使用到了正则,需要经prce编译进去。

3.反向代理

正向代理的概念:也就是传说中的代理,它的工作原理就像一个跳板,简单的说,我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器呢,他能访问那个我不能访问的网站,于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我。从网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。

反向代理的概念:对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理发送请求,反向代理将请求转交给后台的服务端,并将获得的内容返回给客户端,就像这些内容原本就是它自己的一样。如下图所示:

1.初步认识nginx_第5张图片

好处:正向代理的典型用途为在防火墙内的局域网客户端提供方位Internet的途径。正向代理还可以使用缓冲特性减少网络使用率。方向代理的典型用途是将防火墙后面的服务器提供给Internet用户访问。反向代理还可以为后端的多台服务器提供负载平衡,或为后端较慢的服务器提供缓冲服务。另外,反向代理还可以启用高级URL策略和管理技术,从而使处于不同web服务器系统的web页面同时存在于同一个URL空间下。

反向代理的例子:

用python搭建一个简单服务端,代码如下:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options

define("port", default=8000, help="run on the given port", type=int)

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("hello, world\n")

def main():
    tornado.options.parse_command_line()
    application = tornado.web.Application([ \
        (r"/", MainHandler), \
    ])
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == '__main__':
    main()

# vim: set expandtab sw=4 ts=4 sts=4:
分别启动两个端口的服务

python test.py --port=8001
python test.py --port=8002
设定负载均衡服务列表

upstream test_1{
          #ip_hash;
          server 127.0.0.1:8001 down;
          server 127.0.0.1:8002 max_fails=3 fail_timeout=20s;
     }
upstream是Nginx的HTTP Upstream模块,这个模块通过一个简单的调度算法来实现客户端IP到后台服务端的负载均衡。Nginx的负载均衡模块目前支持4种调度算法,下面分别进行介绍:

(1)轮询(默认)。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。Weight指定轮询权值,Weight值越大,分配到的访问几率越高,主要用于后端每个服务器性能不均的情况下。

(2)ip_hash。每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享的问题。

(3)fair。这是比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块。

(4)url_hash。此方法按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身是不支持url_hash 的,如果需要使用这种调度算法,必须安装Nginx的hash软件包。

upstream支持的状态参数

在HTTP Upstream模块中,可以通过server指令指定后端服务器的IP地址和端口,同时还可以设定每个后端服务器在负载均衡调度中的状态。常用的状态有:

down,表示当前的server暂时不参与负载均衡。

backup,预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因此这台机器的压力最轻。

max_fails,允许请求失败的次数,默认为1.当超过最大次数时,返回proxy_next_upstream模块定义的错误。

fail_timeout,在经历了max_fails次失败后,暂停服务的时间。max_fails可以和fail_timeout一起使用。

注,当负载调度算法为ip_hash时,后端服务器在负载调度中的状态不能是weight和backup。

在server模块中的配置:

         location / {
             #root   html;
             #index  index.html index.htm;
             proxy_pass http://test_1;
             proxy_read_timeout 1800;
             proxy_pass_header Server;
             proxy_set_header Host $http_host;
             proxy_redirect off;
             proxy_set_header X-Real-IP $remote_addr; #指令允许将发送到被代理服务器的请求头重新定义或者增加一些字段
             proxy_set_header X-Scheme $scheme;
         }
通过查看日志,可以看到对Nginx的请求,被分配到不同的服务器上。





你可能感兴趣的:(Nginx学习笔记)