Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
下面是一個(gè)完整的 http 塊的配置示例:
http {
# 設(shè)置worker進(jìn)程數(shù)
worker_processes auto;
# 設(shè)置每個(gè)worker進(jìn)程最大連接數(shù)
worker_connections 512;
# 設(shè)置長(zhǎng)連接超時(shí)時(shí)間
keepalive_timeout 65;
# 設(shè)置server_name的hash桶大小
server_names_hash_bucket_size 128;
# 設(shè)置客戶端請(qǐng)求頭緩沖區(qū)大小
client_header_buffer_size 2k;
}
用于配置具體的虛擬主機(jī),包括監(jiān)聽(tīng)的端口、域名、SSL證書(shū)、反向代理等。主要的server配置項(xiàng)包括:
下面是一個(gè)簡(jiǎn)單的server塊的配置示例:
server {
# 監(jiān)聽(tīng)的端口和IP地址
listen 80;
# 指定虛擬主機(jī)的域名
server_name example.com www.example.com;
# 網(wǎng)站的根目錄
root /var/www/example.com;
# 默認(rèn)的首頁(yè)文件
index index.html;
# 訪問(wèn)日志的保存路徑和格式
access_log /var/log/nginx/example.access.log combined;
# 錯(cuò)誤日志的保存路徑和格式
error_log /var/log/nginx/example.error.log;
location /images/ {
root /data/www;
}
location /images/ {
alias /data/www/images/;
}
location / {
if ($request_method=POST) {
return 405;
}
}
location / {
limit_except GET POST {
deny all;
}
}
location / {
proxy_pass http://backend;
}
location / {
root /data/www;
}
location / {
try_files $uri $uri/ /index.html;
}
location / {
rewrite ^/admin/(.*)$ /$1 break;
}
Nginx 的內(nèi)部結(jié)構(gòu)是由核心部分和一系列的功能模塊所組成。這樣劃分是為了使得每個(gè)模塊的功能相對(duì)簡(jiǎn)單,便于開(kāi)發(fā),同時(shí)也便于對(duì)系統(tǒng)進(jìn)行功能擴(kuò)展。Nginx 將各功能模塊組織成一條鏈,當(dāng)有請(qǐng)求到達(dá)的時(shí)候,請(qǐng)求依次經(jīng)過(guò)這條鏈上的部分或者全部模塊,進(jìn)行處理。例如前面講到的 http 請(qǐng)求,會(huì)有11個(gè)處理階段,而每個(gè)階段有對(duì)應(yīng)著許多在此階段生效的模塊對(duì)該 http 請(qǐng)求進(jìn)行處理。同時(shí),Nginx 開(kāi)放了第三方模塊編寫(xiě)功能,用戶可以自定義模塊,控制 http 請(qǐng)求的處理與響應(yīng),這種高度可定制化催生了 Nginx 的大量第三方模塊,也使得 Nginx 定制化開(kāi)發(fā)在各大互聯(lián)網(wǎng)公司十分流行。
關(guān)于 Nginx 模塊的分類(lèi)有很多種方式,目前網(wǎng)上博客中寫(xiě)的較多的是按照功能進(jìn)行分類(lèi),有如下幾大類(lèi):
對(duì)于官方提供的模塊,我們可以直接在官網(wǎng)文檔上學(xué)習(xí),學(xué)習(xí)的方式和學(xué)習(xí)其他互聯(lián)網(wǎng)組件的方式一致,首先學(xué)習(xí)如何使用,在用至熟練后可以深入分析其源碼了解功能實(shí)現(xiàn)背后的原理。
我們以前面介紹到的 Nginx 的限速模塊(limit_req模塊)進(jìn)行說(shuō)明。首先是掌握該模塊的用法,在該模塊的官方地址中,有關(guān)于該模塊的詳細(xì)介紹,包括該模塊提供的所有指令以及所有變量說(shuō)明。此外,還有比較豐富的指令用例。在多次使用該指令并自認(rèn)為掌握了該模塊的用法之后,想了解限速背后的原理以及相關(guān)算法時(shí),就可以深入到源碼學(xué)習(xí)了。
進(jìn)入 Nginx 的源碼目錄,使用ls查看源碼文件,限速模塊是在 http 目錄中的。
[root@server nginx-1.17.6]# cd src/
[root@server src]# ls
core event http mail misc os stream
[root@server src]# ls http/modules/ngx_http_limit_*.c
http/modules/ngx_http_limit_conn_module.c
http/modules/ngx_http_limit_req_module.c
代碼塊12345678
找到 Nginx 模塊對(duì)應(yīng)的代碼文件后,我們就可以閱讀里面的代碼進(jìn)行學(xué)習(xí)。往往源碼的閱讀是枯燥無(wú)味的,我們可以借助海量的網(wǎng)絡(luò)資源輔助我們學(xué)習(xí)。這里就有一篇文章,作者深入分析了 Nginx 的限流模塊的源碼以及相應(yīng)限流算法,最后進(jìn)行了相關(guān)的實(shí)驗(yàn)測(cè)試。通過(guò)這樣一個(gè)個(gè)模塊深入學(xué)習(xí),最后在使用每一個(gè) Nginx 指令時(shí),也會(huì)非常熟練,最后成為 Nginx 高手。
這里我們演示在 Nginx 中使用第三方模塊。 Openresty 社區(qū)提供了一款 Nginx 中的 Echo 模塊,即echo-nginx-module。在 Nginx 中添加了該模塊后,我們?cè)谂渲梦募锌梢允褂迷撃K提供的 echo 指令返回用戶響應(yīng),簡(jiǎn)單方便。該模塊的源碼在 github 上,并且有良好的文檔和使用示例,非常方便開(kāi)發(fā)者使用。
現(xiàn)在我們?cè)?Nginx 的源碼編譯階段加入該第三方模塊,具體操作如下:
[root@server shencong]# pwd
/root/shencong
[root@server shencong]# mkdir nginx-echo
# 下載 nginx 源碼包和第三方模塊的源碼包
[root@server shencong]# wget http://nginx.org/download/nginx-1.17.6.tar.gz
[root@server shencong]# wget https://github.com/openresty/echo-nginx-module/archive/v0.62rc1.tar.gz
# 解壓
[root@server shencong]# tar -xzf nginx-1.17.6.tar.gz
[root@server shencong]# tar -xzf v0.62rc1.tar.gz
[root@server shencong]# ls
echo-nginx-module-0.62rc1 nginx-1.17.6 nginx-1.17.6.tar.gz nginx-echo v0.62rc1.tar.gz
[root@server shencong]# cd nginx-1.17.6
# 使用--add-module添加第三方模塊,參數(shù)為第三方模塊源碼
[root@server shencong]# ./configure --prefix=/root/shencong/nginx-echo --add-module=/root/shencong/echo-nginx-module-0.62rc1
編譯完成后,我們就可以去nginx-echo目錄中的 nginx.conf文件中添加echo 指令 。準(zhǔn)備如下的配置(可以參參考社區(qū)提供的示例):
...
http {
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
# 新增測(cè)試 echo 指令配置
location /timed_hello {
default_type text/plain;
echo_reset_timer;
echo hello world;
echo "'hello world' takes about $echo_timer_elapsed sec.";
echo hiya igor;
echo "'hiya igor' takes about $echo_timer_elapsed sec.";
}
location /echo_with_sleep {
default_type text/plain;
echo hello world;
echo_flush; # ensure the client can see previous output immediately
echo_sleep 2.5; # in sec
echo "'hello' takes about $echo_timer_elapsed sec.";
}
}
}
...
啟動(dòng) Nginx 后,我們就可以在瀏覽器上請(qǐng)求者兩個(gè) URI 地址,看到相應(yīng) echo 返回的信息了。第二個(gè)配置是使用了 echo_sleep 指令,會(huì)使得請(qǐng)求在休眠 2.5s 后才返回。
想要編寫(xiě) Nginx 模塊,首先需要對(duì) Nginx 模塊中的源碼以及相關(guān)的數(shù)據(jù)結(jié)構(gòu)有所了解,還要知曉 Nginx HTTP 模塊的調(diào)用流程。假設(shè)我要實(shí)現(xiàn)前面第三方模塊Echo的最簡(jiǎn)單形式,即只輸出相應(yīng)的字符串即可。假定模塊支持的指令名稱(chēng)還是 echo, 這個(gè) echo 指令需要跟一個(gè)參數(shù),即輸出的字符串。我們需要做如下幾步:
static ngx_command_t ngx_http_echo_commands[]={
{ ngx_string("echo"), /* 指令名稱(chēng),利用ngx_string宏定義 */
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, /* 用在 location 指令塊內(nèi),且有1個(gè)參數(shù) */
ngx_http_echo, /* 處理回調(diào)函數(shù) */
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, ed), /* 指定參數(shù)讀取位置 */
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;
/* 找到指令所屬的配置塊,這里我們限定echo指令的上下文環(huán)境只有l(wèi)ocation */
clcf=ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
/* 指定處理的handler */
clcf->handler=ngx_http_echo_handler;
...
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_echo_handler(ngx_http_request_t *r)
{
...
/* 向用戶發(fā)送相應(yīng)包 */
return ngx_http_output_filter(r, &out);
}
/* Http context of the module */
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 */
};
/* Module */
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
};
完成以上幾步,一個(gè)簡(jiǎn)易的自定義模塊就算大功告成了。接下來(lái)我們動(dòng)手完成第一個(gè)自定義的模塊,將其編譯進(jìn)Nginx 二進(jìn)制文件中并進(jìn)行測(cè)試。
我們來(lái)完成一個(gè)簡(jiǎn)單的自定義 http 模塊,來(lái)實(shí)現(xiàn)前面Echo模塊的最簡(jiǎn)單形式,即使用指令輸出 “hello, world” 字符串。首先新建一個(gè)目錄echo-nginx-module,然后在目錄下新建兩個(gè)文件config和ngx_http_echo_module.c
[root@server echo-nginx-module]# pwd
/root/shencong/echo-nginx-module
[root@server echo-nginx-module]# ls
config ngx_http_echo_module.c
兩個(gè)文件內(nèi)容分別如下:
[root@server echo-nginx-module]# cat config
ngx_addon_name=ngx_http_echo_module
# 指定模塊名稱(chēng)
HTTP_MODULES="$HTTP_MODULES ngx_http_echo_module"
# 指定模塊源碼路徑
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_echo_module.c"
[root@server echo-nginx-module]# cat ngx_http_echo_module.c
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
/* Module config */
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"), /* 指令名稱(chēng),利用ngx_string宏定義 */
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, /* 用在 location 指令塊內(nèi),且有1個(gè)參數(shù) */
ngx_http_echo, /* 處理回調(diào)函數(shù) */
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, ed), /* 指定參數(shù)讀取位置 */
NULL },
ngx_null_command
};
/* Http context of the module */
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 */
};
/* Module */
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
};
/* Handler function */
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;
/* 獲取指令的參數(shù) */
elcf=ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
if(!(r->method & (NGX_HTTP_HEAD|NGX_HTTP_GET|NGX_HTTP_POST)))
{
/* 如果不是 HEAD/GET/PUT 請(qǐng)求,則返回405 Not Allowed錯(cuò)誤 */
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;
}
/* 向用戶發(fā)送相應(yīng)包 */
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);
/* 指定處理的handler */
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;
}
這樣一個(gè)第三方模塊包就完成了,接下來(lái)我們要向之前使用第三方模塊一樣,將它編譯進(jìn) Nginx,具體操作如下。
[root@server shencong]# cd nginx-1.17.6/
[root@server nginx-1.17.6]# ./configure --prefix=/root/shencong/nginx-echo --add-module=/root/shencong/echo-nginx-module
...
[root@server nginx-1.17.6] # make && make install
...
[root@server nginx-1.17.6]# cd ../nginx-echo/sbin/
[root@server sbin]# ./nginx -V
nginx version: nginx/1.17.6
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
configure arguments: --prefix=/root/shencong/nginx-echo --add-module=/root/shencong/echo-nginx-module
接下來(lái),我們只要在 nginx.conf 中加入我們的指令,并給一個(gè)參數(shù),就能看到我們自定義的輸出了。
...
http {
...
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /test {
echo hello,world;
}
...
}
}
...
最后我們請(qǐng)求主機(jī)的80端口,URI=/test,瀏覽器輸出"hello, world",說(shuō)明我們的自定義模塊成功了!
在 Nginx 基礎(chǔ)架構(gòu)介紹的最后,主要是介紹了 Nginx 的模塊設(shè)計(jì)以及相應(yīng)的模塊用法。最后,我們簡(jiǎn)單給出了一個(gè)簡(jiǎn)單編寫(xiě)自己模塊的案例作為本章的實(shí)戰(zhàn)案例。希望這一節(jié)之后,大家能對(duì) Nginx 的模塊化設(shè)計(jì)有所了解,并有興趣在模塊的源碼方向深入學(xué)習(xí)。
篇介紹比較基礎(chǔ)的Nginx常用命令及配置。
#假設(shè)Nginx的安裝目錄為
/home/nginx/nginx/
#啟動(dòng)命令
/home/nginx/nginx/sbin/nginx -c /home/nginx/nginx/conf/nginx.conf
#狀態(tài)檢查
/home/nginx/nginx/sbin/nginx -t
#重新加載配置
/home/nginx/nginx/sbin/nginx -s reload
#關(guān)閉
#查詢nginx主進(jìn)程號(hào)
ps -ef | grep nginx
#從容停止
kill -QUIT 主進(jìn)程號(hào)
#快速停止
kill -TERM 主進(jìn)程號(hào)
#強(qiáng)制停止
kill -9 主進(jìn)程號(hào)
#若nginx.conf配置了pid文件路徑,如果沒(méi)有,則在logs目錄下
kill -信號(hào)類(lèi)型 '/usr/local/nginx/logs/nginx.pid'
events事件驅(qū)動(dòng)配置
events {
use epoll; #使用epoll類(lèi)型的IO多路復(fù)用模型,性能比select高
worker_connections 204800; #Worker進(jìn)程能夠打開(kāi)的最大并發(fā)連接數(shù)
accept_mutex on; #各個(gè)Worker進(jìn)程通過(guò)鎖來(lái)獲取新連接
}
server虛擬主機(jī)配置
server {
listen 80;
server_name admin.cloudxue.com; #后臺(tái)管理服務(wù)的域名前綴
location / {
default_type 'text/html';
charset utf-8;
echo "this is admin server";
}
}
server {
listen 80;
server_name file.cloudxue.com; #文件服務(wù)的域名前綴
location / {
default_type 'text/html';
charset utf-8;
echo "this is file server";
}
}
server {
listen 80 default;
server_name cloudxue.com *.cloudxue.com; #若未指定前綴,配置默認(rèn)訪問(wèn)的虛擬主機(jī)
location / {
default_type 'text/html';
charset utf-8;
echo "this is default server";
}
}
多個(gè)虛擬主機(jī)之間根絕server_name匹配的優(yōu)先級(jí)從高到低:
錯(cuò)誤頁(yè)面配置
error_page指令,該指令可用于http、server、location、if in location等上下文。
server {
listen 80;
server_name admin.cloudxue.com; #后臺(tái)管理服務(wù)的域名前綴
root /usr/local/openresty/www;
location / {
default_type 'text/html';
charset utf-8;
echo "this is admin server";
}
#設(shè)置錯(cuò)誤頁(yè)面
#error_page 404 /404.html;
error_page 404=200 /404.html #防止404頁(yè)面被劫持
error_page 500 502 503 504 /50x.html;
}
長(zhǎng)鏈接配置
keepalive_timeout 75; #長(zhǎng)鏈接有效時(shí)長(zhǎng),0表示禁用長(zhǎng)鏈接,默認(rèn)75秒
keepalive_requests 100; #一條長(zhǎng)鏈接上允許被請(qǐng)求的資源的最大數(shù)量,默認(rèn)100
訪問(wèn)日志配置
access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
http {
#先定義日志格式,名為為
log_format format_main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#日志文件、日志訪問(wèn)格式
access_log logs/access_main.log format_main;
}
location路由規(guī)則配置
rewrite模塊指令配置
Nginx的rewrite模塊即ngx_http_rewrite_module標(biāo)準(zhǔn)模塊,默認(rèn)安裝的模塊。主要功能是對(duì)URI進(jìn)行重寫(xiě),然后再一次進(jìn)行l(wèi)ocation匹配或者直接進(jìn)行30X重定向返回給客戶端。
set $variable value
#實(shí)例
set $a "foo";
set $b "$a, $a";
rewrite regrex replacement [flag];
#實(shí)例
location /download/ {
rewrite ^/download/(.*)/video/(.*)$ /view/$1/mp3/$2.mp3/ last;
rewrite ^/download/(.*)/audio/(.*)$ /view/$1/mp3/$2.rmvb/ last;
return 404;
}
location /view {
echo "uri: $uri ";
}
#訪問(wèn)如下地址
curl http://demo.cloudxue.com/download/1/video/10
#rewrite模塊進(jìn)行匹配后,占位變量$1的值為1, $2的值為10
uri: /view/1/mp3/10.mp3
如果同一個(gè)上下文中出現(xiàn)多個(gè)rewrite指令,匹配會(huì)按照rewrite指令出現(xiàn)的順序先后依次進(jìn)行下去,匹配成功后并不會(huì)終止,而是繼續(xù)往下匹配,知道返回最后一個(gè)匹配為止。可以通過(guò)指令參數(shù)flag控制是否中途停止匹配,假設(shè)在location上下文中:
注意事項(xiàng):
if (condition) {...}
#實(shí)例
location /if_demo {
if ($http_user_agent ~*"Firefox") { #匹配Firefox瀏覽器
return 403;
}
if ($http_user_agent ~*"Chrome") { #匹配Chrome瀏覽器
return 301;
}
if ($http_user_agent ~*"iphone") { #匹配iPhone手機(jī)
return 302;
}
if ($http_user_agent ~*"android") { #匹配安卓手機(jī)
return 404;
}
return 405; #其他瀏覽器默認(rèn)規(guī)則
}
#格式1:返回響應(yīng)的狀態(tài)碼和提示文字,提示文字可選
return code [text];
#格式2:返回響應(yīng)的重定向狀態(tài)碼和重定向URL
return code URL;
#格式3:返回響應(yīng)的重定向URL,默認(rèn)的返回狀態(tài)碼是臨時(shí)重定向302
return URL;
使用Ajax進(jìn)行跨域請(qǐng)求時(shí),瀏覽器會(huì)向跨域資源的服務(wù)端發(fā)送一個(gè)OPTION請(qǐng)求,用于判斷實(shí)際請(qǐng)求是否安全或者判斷服務(wù)端是否允許跨域訪問(wèn),這種請(qǐng)求也叫預(yù)檢請(qǐng)求。跨域訪問(wèn)的預(yù)檢請(qǐng)求是瀏覽器自動(dòng)發(fā)出的,用戶程序不知情,如果不進(jìn)行特殊的配置,那么客戶端發(fā)出一次請(qǐng)求,在服務(wù)端會(huì)收到兩個(gè)請(qǐng)求:一個(gè)是預(yù)檢請(qǐng)求,一個(gè)是正式的請(qǐng)求。會(huì)比較影響性能,通常Nginx代理服務(wù)器對(duì)預(yù)檢請(qǐng)求進(jìn)行攔截,同時(shí)對(duì)預(yù)檢請(qǐng)求設(shè)置比較長(zhǎng)時(shí)間的有效期。
add_header Cache-Control no-store;
add_header Content-Encoding gzip;
add_header Content-Type 'text/html; charset=utf-8';
#實(shí)例
upstream zuul {
server "192.168.233.122:7799";
keepalive 1000;
}
server {
listen 80;
server_name nginx.server *.nginx.server;
default_type 'text/html';
charset utf-8;
#轉(zhuǎn)發(fā)到上游服務(wù)器,但是‘OPTIONS’請(qǐng)求直接返回空
location / {
add_header Access-Control-Max-Age 1728000; #指定本次預(yù)檢請(qǐng)求的有效期,單位秒,允許緩存該條回應(yīng)20天,此期間內(nèi)客戶端不用發(fā)出另一條預(yù)檢請(qǐng)求
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Keep-Alive, User-Agent, X-Requested-With, \
If-Modified-Since, Cache-Control, COntent-Type,token';
return 204;
}
proxy_pass http://zuul/;
}
location /sequence_demo {
set $a foo;
echo $a;
set $a bar;
echo $a;
}
#訪問(wèn)測(cè)試:
curl http://cloudxue.com/sequence_demo
#響應(yīng)
bar bar
#若按照請(qǐng)求處理階段的先后次序排序
location /sequence_demo {
#rewrite 階段的配置指令,執(zhí)行在前面
set $a foo;
set $a bar;
#content階段的配置指令,執(zhí)行在后面
echo $a;
echo $a;
}
#所以以上輸出為 bar bar
proxy_pass反向代理指令
位于ngx_http_proxy_module模塊,注冊(cè)在HTTP請(qǐng)求11個(gè)階段的content階段。
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
server_name localhost;
default_type 'text/html';
charset utf-8;
location / {
echo "-uri=$uri"
"-host=$host"
"-remote=$remote_addr"
"-proxy_add_x_forwarded=$proxy_add_x_forwarded_for"
"-http_x_forwarded_for=$http_x_forwarded_for";
}
}
server {
listen 80;
server_name localhost;
default_type 'text/html';
charset utf-8;
location / {
echo "默認(rèn)根路徑匹配:/";
}
#不帶location前綴的代理類(lèi)型
location /foo_no_prefix {
proxy_pass http://127.0.0.1:8080/;
}
#帶location前綴的代理類(lèi)型
location /foo_prefix {
proxy_pass http://127.0.0.1:8080;
}
#帶部分URI路徑的代理
location /foo_uri_1 {
proxy_pass http://127.0.0.1:8080/contextA/;
}
#帶部分URI路徑的代理
location /foo_uri_2 {
proxy_pass http://127.0.0.1:8080/context-A;
}
}
}
測(cè)試結(jié)果如下
? ~ curl http://127.0.0.1/foo_no_prefix/bar.html
-uri=/bar.html -host=127.0.0.1 -remote=127.0.0.1 -proxy_add_x_forwarded=127.0.0.1 -http_x_forwarded_for=? ~ curl http://127.0.0.1/foo_prefix/bar.html
-uri=/foo_prefix/bar.html -host=127.0.0.1 -remote=127.0.0.1 -proxy_add_x_forwarded=127.0.0.1 -http_x_forwarded_for=? ~ curl http://127.0.0.1/foo_uri_1/bar.html
-uri=/contextA/bar.html -host=127.0.0.1 -remote=127.0.0.1 -proxy_add_x_forwarded=127.0.0.1 -http_x_forwarded_for=? ~ curl http://127.0.0.1/foo_uri_2/bar.html
-uri=/context-A/bar.html -host=127.0.0.1 -remote=127.0.0.1 -proxy_add_x_forwarded=127.0.0.1 -http_x_forwarded_for=? ~
proxy_set_header請(qǐng)求頭設(shè)置指令
在反向代理前,proxy_set_header指令能重新定義添加字段傳遞給代理服務(wù)的請(qǐng)求頭。請(qǐng)求頭的值可以包含文本、變量和它們的組合,格式如下
proxy_set_header head_field field_value;
該指令在發(fā)生反向代理之前,將保持在內(nèi)置變量$remote_addr中的真實(shí)客戶端地址保持到請(qǐng)求頭中,通常請(qǐng)求頭參數(shù)名為X-real-ip。
在Java端可以使用request.getHeader("X-real-ip")獲取請(qǐng)求頭的值,就可以拿到客戶的真實(shí)IP。
由于在整個(gè)請(qǐng)求處理鏈條上可能不僅一次反向代理,可能會(huì)經(jīng)過(guò)N次反向代理,為了獲取整個(gè)轉(zhuǎn)發(fā)記錄,可以使用$proxy_add_x_forwarded_for內(nèi)置變量,該值的第一個(gè)就是真實(shí)地址
為了不丟失信息,反向代理的設(shè)置如下:
location /hello {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off; #修改從上游被代理服務(wù)器傳來(lái)的應(yīng)答頭中的Location和Refresh字段
}
以上配置,設(shè)置了Host、X-real-ip、X-Forwarded-For,分別將當(dāng)前的目標(biāo)主機(jī)、客戶端IP、轉(zhuǎn)發(fā)記錄保存在請(qǐng)求頭中。
當(dāng)上游服務(wù)器返回的響應(yīng)碼是重定向301或者刷新302請(qǐng)求時(shí),proxy_redirect指令可以重設(shè)HTTP頭部的location或refresh字段值,off參數(shù)表示禁止所有的proxy_redirect指令,即反向代理時(shí),禁止重定向。
作為Nginx的一大特色,若沒(méi)有負(fù)載均衡,只有反向代理,那么其使用價(jià)值會(huì)大打折扣。Nginx在配置反向代理時(shí),可以通過(guò)負(fù)載均衡機(jī)制配置一個(gè)上有服務(wù)器組。當(dāng)組內(nèi)某臺(tái)機(jī)器宕機(jī)時(shí),仍能維持系統(tǒng)可用,從而實(shí)現(xiàn)高可用。
Nginx的負(fù)載均衡配置主要用到upstream指令,其格式為:
upstream name {
server name address [parameters];
}
#上下文為http配置快,內(nèi)部使用server指令定義組內(nèi)的上游候選服務(wù)器
配置示例:
upstream zuul {
#名為upstream_zuul的共享內(nèi)存區(qū),大小為64k
zone upstream_zuul 64k;
#組內(nèi)該機(jī)器的權(quán)重為5,最大并發(fā)連接數(shù)為500
server "192.168.223.121:7799" weight=5 max_conns=500;
#組內(nèi)該機(jī)器的默認(rèn)權(quán)重為1,同時(shí)設(shè)置20秒內(nèi)失敗2次,判定該服務(wù)器不可用
server "192.168.233.122:7799" fail_timeout=20s max_fails=2;
#后備服務(wù)
server "192.168.233.123:7799" backup;
}
upstream的負(fù)載分配方式
upstream backend {
#通過(guò)請(qǐng)求的$request_uri的hash值進(jìn)行負(fù)載均衡
hash $request_uri consistent;
server 192.168.233.121;
server 192.168.233.122;
server 192.168.233.123;
}
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。