下面是一個完整的 http 塊的配置示例:
http {
# 設置worker進程數
worker_processes auto;
# 設置每個worker進程最大連接數
worker_connections 512;
# 設置長連接超時時間
keepalive_timeout 65;
# 設置server_name的hash桶大小
server_names_hash_bucket_size 128;
# 設置客戶端請求頭緩沖區大小
client_header_buffer_size 2k;
}
用于配置具體的虛擬主機,包括監聽的端口、域名、SSL證書、反向代理等。主要的server配置項包括:
下面是一個簡單的server塊的配置示例:
server {
# 監聽的端口和IP地址
listen 80;
# 指定虛擬主機的域名
server_name example.com www.example.com;
# 網站的根目錄
root /var/www/example.com;
# 默認的首頁文件
index index.html;
# 訪問日志的保存路徑和格式
access_log /var/log/nginx/example.access.log combined;
# 錯誤日志的保存路徑和格式
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 的內部結構是由核心部分和一系列的功能模塊所組成。這樣劃分是為了使得每個模塊的功能相對簡單,便于開發,同時也便于對系統進行功能擴展。Nginx 將各功能模塊組織成一條鏈,當有請求到達的時候,請求依次經過這條鏈上的部分或者全部模塊,進行處理。例如前面講到的 http 請求,會有11個處理階段,而每個階段有對應著許多在此階段生效的模塊對該 http 請求進行處理。同時,Nginx 開放了第三方模塊編寫功能,用戶可以自定義模塊,控制 http 請求的處理與響應,這種高度可定制化催生了 Nginx 的大量第三方模塊,也使得 Nginx 定制化開發在各大互聯網公司十分流行。
關于 Nginx 模塊的分類有很多種方式,目前網上博客中寫的較多的是按照功能進行分類,有如下幾大類:
對于官方提供的模塊,我們可以直接在官網文檔上學習,學習的方式和學習其他互聯網組件的方式一致,首先學習如何使用,在用至熟練后可以深入分析其源碼了解功能實現背后的原理。
我們以前面介紹到的 Nginx 的限速模塊(limit_req模塊)進行說明。首先是掌握該模塊的用法,在該模塊的官方地址中,有關于該模塊的詳細介紹,包括該模塊提供的所有指令以及所有變量說明。此外,還有比較豐富的指令用例。在多次使用該指令并自認為掌握了該模塊的用法之后,想了解限速背后的原理以及相關算法時,就可以深入到源碼學習了。
進入 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 模塊對應的代碼文件后,我們就可以閱讀里面的代碼進行學習。往往源碼的閱讀是枯燥無味的,我們可以借助海量的網絡資源輔助我們學習。這里就有一篇文章,作者深入分析了 Nginx 的限流模塊的源碼以及相應限流算法,最后進行了相關的實驗測試。通過這樣一個個模塊深入學習,最后在使用每一個 Nginx 指令時,也會非常熟練,最后成為 Nginx 高手。
這里我們演示在 Nginx 中使用第三方模塊。 Openresty 社區提供了一款 Nginx 中的 Echo 模塊,即echo-nginx-module。在 Nginx 中添加了該模塊后,我們在配置文件中可以使用該模塊提供的 echo 指令返回用戶響應,簡單方便。該模塊的源碼在 github 上,并且有良好的文檔和使用示例,非常方便開發者使用。
現在我們在 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添加第三方模塊,參數為第三方模塊源碼
[root@server shencong]# ./configure --prefix=/root/shencong/nginx-echo --add-module=/root/shencong/echo-nginx-module-0.62rc1
編譯完成后,我們就可以去nginx-echo目錄中的 nginx.conf文件中添加echo 指令 。準備如下的配置(可以參參考社區提供的示例):
...
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;
}
# 新增測試 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.";
}
}
}
...
啟動 Nginx 后,我們就可以在瀏覽器上請求者兩個 URI 地址,看到相應 echo 返回的信息了。第二個配置是使用了 echo_sleep 指令,會使得請求在休眠 2.5s 后才返回。
想要編寫 Nginx 模塊,首先需要對 Nginx 模塊中的源碼以及相關的數據結構有所了解,還要知曉 Nginx HTTP 模塊的調用流程。假設我要實現前面第三方模塊Echo的最簡單形式,即只輸出相應的字符串即可。假定模塊支持的指令名稱還是 echo, 這個 echo 指令需要跟一個參數,即輸出的字符串。我們需要做如下幾步:
static ngx_command_t ngx_http_echo_commands[]={
{ ngx_string("echo"), /* 指令名稱,利用ngx_string宏定義 */
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, /* 用在 location 指令塊內,且有1個參數 */
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;
/* 找到指令所屬的配置塊,這里我們限定echo指令的上下文環境只有location */
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)
{
...
/* 向用戶發送相應包 */
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
};
完成以上幾步,一個簡易的自定義模塊就算大功告成了。接下來我們動手完成第一個自定義的模塊,將其編譯進Nginx 二進制文件中并進行測試。
我們來完成一個簡單的自定義 http 模塊,來實現前面Echo模塊的最簡單形式,即使用指令輸出 “hello, world” 字符串。首先新建一個目錄echo-nginx-module,然后在目錄下新建兩個文件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
兩個文件內容分別如下:
[root@server echo-nginx-module]# cat 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"
[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"), /* 指令名稱,利用ngx_string宏定義 */
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, /* 用在 location 指令塊內,且有1個參數 */
ngx_http_echo, /* 處理回調函數 */
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, ed), /* 指定參數讀取位置 */
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;
/* 獲取指令的參數 */
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 請求,則返回405 Not Allowed錯誤 */
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);
/* 指定處理的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;
}
這樣一個第三方模塊包就完成了,接下來我們要向之前使用第三方模塊一樣,將它編譯進 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
接下來,我們只要在 nginx.conf 中加入我們的指令,并給一個參數,就能看到我們自定義的輸出了。
...
http {
...
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /test {
echo hello,world;
}
...
}
}
...
最后我們請求主機的80端口,URI=/test,瀏覽器輸出"hello, world",說明我們的自定義模塊成功了!
在 Nginx 基礎架構介紹的最后,主要是介紹了 Nginx 的模塊設計以及相應的模塊用法。最后,我們簡單給出了一個簡單編寫自己模塊的案例作為本章的實戰案例。希望這一節之后,大家能對 Nginx 的模塊化設計有所了解,并有興趣在模塊的源碼方向深入學習。
篇介紹比較基礎的Nginx常用命令及配置。
#假設Nginx的安裝目錄為
/home/nginx/nginx/
#啟動命令
/home/nginx/nginx/sbin/nginx -c /home/nginx/nginx/conf/nginx.conf
#狀態檢查
/home/nginx/nginx/sbin/nginx -t
#重新加載配置
/home/nginx/nginx/sbin/nginx -s reload
#關閉
#查詢nginx主進程號
ps -ef | grep nginx
#從容停止
kill -QUIT 主進程號
#快速停止
kill -TERM 主進程號
#強制停止
kill -9 主進程號
#若nginx.conf配置了pid文件路徑,如果沒有,則在logs目錄下
kill -信號類型 '/usr/local/nginx/logs/nginx.pid'
events事件驅動配置
events {
use epoll; #使用epoll類型的IO多路復用模型,性能比select高
worker_connections 204800; #Worker進程能夠打開的最大并發連接數
accept_mutex on; #各個Worker進程通過鎖來獲取新連接
}
server虛擬主機配置
server {
listen 80;
server_name admin.cloudxue.com; #后臺管理服務的域名前綴
location / {
default_type 'text/html';
charset utf-8;
echo "this is admin server";
}
}
server {
listen 80;
server_name file.cloudxue.com; #文件服務的域名前綴
location / {
default_type 'text/html';
charset utf-8;
echo "this is file server";
}
}
server {
listen 80 default;
server_name cloudxue.com *.cloudxue.com; #若未指定前綴,配置默認訪問的虛擬主機
location / {
default_type 'text/html';
charset utf-8;
echo "this is default server";
}
}
多個虛擬主機之間根絕server_name匹配的優先級從高到低:
錯誤頁面配置
error_page指令,該指令可用于http、server、location、if in location等上下文。
server {
listen 80;
server_name admin.cloudxue.com; #后臺管理服務的域名前綴
root /usr/local/openresty/www;
location / {
default_type 'text/html';
charset utf-8;
echo "this is admin server";
}
#設置錯誤頁面
#error_page 404 /404.html;
error_page 404=200 /404.html #防止404頁面被劫持
error_page 500 502 503 504 /50x.html;
}
長鏈接配置
keepalive_timeout 75; #長鏈接有效時長,0表示禁用長鏈接,默認75秒
keepalive_requests 100; #一條長鏈接上允許被請求的資源的最大數量,默認100
訪問日志配置
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"';
#日志文件、日志訪問格式
access_log logs/access_main.log format_main;
}
location路由規則配置
rewrite模塊指令配置
Nginx的rewrite模塊即ngx_http_rewrite_module標準模塊,默認安裝的模塊。主要功能是對URI進行重寫,然后再一次進行location匹配或者直接進行30X重定向返回給客戶端。
set $variable value
#實例
set $a "foo";
set $b "$a, $a";
rewrite regrex replacement [flag];
#實例
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 ";
}
#訪問如下地址
curl http://demo.cloudxue.com/download/1/video/10
#rewrite模塊進行匹配后,占位變量$1的值為1, $2的值為10
uri: /view/1/mp3/10.mp3
如果同一個上下文中出現多個rewrite指令,匹配會按照rewrite指令出現的順序先后依次進行下去,匹配成功后并不會終止,而是繼續往下匹配,知道返回最后一個匹配為止。可以通過指令參數flag控制是否中途停止匹配,假設在location上下文中:
注意事項:
if (condition) {...}
#實例
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手機
return 302;
}
if ($http_user_agent ~*"android") { #匹配安卓手機
return 404;
}
return 405; #其他瀏覽器默認規則
}
#格式1:返回響應的狀態碼和提示文字,提示文字可選
return code [text];
#格式2:返回響應的重定向狀態碼和重定向URL
return code URL;
#格式3:返回響應的重定向URL,默認的返回狀態碼是臨時重定向302
return URL;
使用Ajax進行跨域請求時,瀏覽器會向跨域資源的服務端發送一個OPTION請求,用于判斷實際請求是否安全或者判斷服務端是否允許跨域訪問,這種請求也叫預檢請求。跨域訪問的預檢請求是瀏覽器自動發出的,用戶程序不知情,如果不進行特殊的配置,那么客戶端發出一次請求,在服務端會收到兩個請求:一個是預檢請求,一個是正式的請求。會比較影響性能,通常Nginx代理服務器對預檢請求進行攔截,同時對預檢請求設置比較長時間的有效期。
add_header Cache-Control no-store;
add_header Content-Encoding gzip;
add_header Content-Type 'text/html; charset=utf-8';
#實例
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;
#轉發到上游服務器,但是‘OPTIONS’請求直接返回空
location / {
add_header Access-Control-Max-Age 1728000; #指定本次預檢請求的有效期,單位秒,允許緩存該條回應20天,此期間內客戶端不用發出另一條預檢請求
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;
}
#訪問測試:
curl http://cloudxue.com/sequence_demo
#響應
bar bar
#若按照請求處理階段的先后次序排序
location /sequence_demo {
#rewrite 階段的配置指令,執行在前面
set $a foo;
set $a bar;
#content階段的配置指令,執行在后面
echo $a;
echo $a;
}
#所以以上輸出為 bar bar
proxy_pass反向代理指令
位于ngx_http_proxy_module模塊,注冊在HTTP請求11個階段的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 "默認根路徑匹配:/";
}
#不帶location前綴的代理類型
location /foo_no_prefix {
proxy_pass http://127.0.0.1:8080/;
}
#帶location前綴的代理類型
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;
}
}
}
測試結果如下
? ~ 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請求頭設置指令
在反向代理前,proxy_set_header指令能重新定義添加字段傳遞給代理服務的請求頭。請求頭的值可以包含文本、變量和它們的組合,格式如下
proxy_set_header head_field field_value;
該指令在發生反向代理之前,將保持在內置變量$remote_addr中的真實客戶端地址保持到請求頭中,通常請求頭參數名為X-real-ip。
在Java端可以使用request.getHeader("X-real-ip")獲取請求頭的值,就可以拿到客戶的真實IP。
由于在整個請求處理鏈條上可能不僅一次反向代理,可能會經過N次反向代理,為了獲取整個轉發記錄,可以使用$proxy_add_x_forwarded_for內置變量,該值的第一個就是真實地址
為了不丟失信息,反向代理的設置如下:
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; #修改從上游被代理服務器傳來的應答頭中的Location和Refresh字段
}
以上配置,設置了Host、X-real-ip、X-Forwarded-For,分別將當前的目標主機、客戶端IP、轉發記錄保存在請求頭中。
當上游服務器返回的響應碼是重定向301或者刷新302請求時,proxy_redirect指令可以重設HTTP頭部的location或refresh字段值,off參數表示禁止所有的proxy_redirect指令,即反向代理時,禁止重定向。
作為Nginx的一大特色,若沒有負載均衡,只有反向代理,那么其使用價值會大打折扣。Nginx在配置反向代理時,可以通過負載均衡機制配置一個上有服務器組。當組內某臺機器宕機時,仍能維持系統可用,從而實現高可用。
Nginx的負載均衡配置主要用到upstream指令,其格式為:
upstream name {
server name address [parameters];
}
#上下文為http配置快,內部使用server指令定義組內的上游候選服務器
配置示例:
upstream zuul {
#名為upstream_zuul的共享內存區,大小為64k
zone upstream_zuul 64k;
#組內該機器的權重為5,最大并發連接數為500
server "192.168.223.121:7799" weight=5 max_conns=500;
#組內該機器的默認權重為1,同時設置20秒內失敗2次,判定該服務器不可用
server "192.168.233.122:7799" fail_timeout=20s max_fails=2;
#后備服務
server "192.168.233.123:7799" backup;
}
upstream的負載分配方式
upstream backend {
#通過請求的$request_uri的hash值進行負載均衡
hash $request_uri consistent;
server 192.168.233.121;
server 192.168.233.122;
server 192.168.233.123;
}
*請認真填寫需求信息,我們會在24小時內與您取得聯系。