nginx根據真實IP分發請求


nginx根據真實IP分發請求

使用場景

2022年6月份,社保局接收到上級的文件,要求建立統一的門戶系統(所有的用戶都通過門戶系統登錄到子系統,原子系統的用戶、角色、機構、權限等,都交給門戶網站來控制)。於是各個子系統就需要做一個適配性的改造,子系統有機關養老、企業養老、城鄉居民養老、工傷保險、失業保險等。在子系統改造的過程中,除了代碼層面的改造以外我們遇上了幾個關於負載方面的問題。

傳統運行方式

會話保持(低成本方式)解決session問題

在原來的子系統中,為了一定程度上解決服務器壓力的問題,我們其實已經用nginx做了一個代理,兩個負載地址作為雙活部署。目前的系統有個做的不是很好的地方,就是在session里面塞了不少的東西,如果客戶端的請求第一次發到了server1上,第二次發到了server2上,很有可能就因為這兩個會話的不同,導致里面的參數獲取不到,所以我們要考慮session共享或者會話保持的問題。其實這個問題已經解決了,項目組選擇的方式是會話保持。具體的方法就是在配置nginx的時候,請求的分發策略選擇ip-hash,這樣一來,一個ip在兩台服務器都運行正常的情況下,永遠只會發到某一台上。一旦某一台服務器宕機,其實上述的問題還是會出現,但是我們就不考慮這個情況了,畢竟出現的概率比較小,這邊的成本想控制到最低。

# nginx配置如下
upstream jgyl{
	zone upstreams 64K;
	ip-hash
	server 10.40.29.153:8080;
	server 10.40.29.126:8080;
}

問題復現

nginx代理失效

在參與門戶系統的集成中,請求的方式發生了如下變化:

原:客戶端請求 → nginx → 負載服務器

現:客戶端請求 → 門戶網關 → 【用戶認證中心 → 門戶網關】 → nginx → 負載服務器

其中訪問用戶認證中心主要是為了獲取用戶認證的標識,即token,有token的時候就不需要括號中的部分了。

在現有的請求方式下,nginx認為所有的請求都來自門戶網關而不是,客戶端,所以所有分發的請求都會發到一台服務器上面,於是nginx的代理在實際層面上就失效了。

解決辦法

在解決這個問題的時候,我們討論了幾種解決方式。在傳統方式的基礎下,session利用redis來共享顯然是不太好的,因為代價比較大,而且redis必須安裝在一個共享的目錄里面。利用數據庫共享看似不錯,但是網上的解決方案完全不夠成熟。所以我們還是想看看有沒有辦法讓它還原到原來的運行模式上,總之百轉千回,終於找到了解決方案。那就是讓nginx根據真實的IP的分發請求。

X-Forwarded-ForXFF)是用來識別通過HTTP代理或負載均衡方式連接到Web服務器的客戶端最原始的IP地址的HTTP請求頭字段,我們的核心思路就是在這個字段里面獲取最早的一個請求,原理如下:

1、客戶端請求(ip0) → 服務器(ip1)。request.getHeader("X-Forwarded-For"); // 空

2、客戶端請求(ip0) → nginx(ip1)→ 服務器(ip2)。request.getHeader("X-Forwarded-For"); // ip0

3、客戶端請求(ip0) → 請求轉發器1(ip1) → 請求轉發器2(ip2) → nginx(ip3)→ 服務器(ip4)。

request.getHeader("X-Forwarded-For"); // ip0,ip1,ip2

其中ip0是最初始的IP請求,ip-hash默認以ip2作為請求IP來分發請求。

所以我們通過這個字段來加工出ip0,之后用它來分發請求就可以了,核心的配置如下。

# nginx配置如下
map $http_x_forwarded_for  $clientRealIp {
	"" $remote_addr;
	~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
	
upstream jgyl{
	zone upstreams 64K;
	hash  $clientRealIp;
	server 10.40.29.153:8080;
	server 10.40.29.126:8080;
}

完整的配置

這里要注意X-Forwarded-For雖然是一個可用的標准,但是並非所有途中的轉發器都會增加值,需要每個轉發器都設置上(以nginx舉例):

proxy_set_header Host $host:$server_port; # 傳 header 參數至后端服務
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 設置request header 即客戶端IP地址


#user  nobody;
worker_processes  auto;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
error_log  logs/error.log  info;

pid        logs/nginx.pid;

worker_rlimit_nofile 65535;
events {
	use	epoll;
    worker_connections	65535;
}


http {

    map $http_x_forwarded_for  $clientRealIp {
       ""      $remote_addr;
       ~^(?P<firstAddr>[0-9\.]+),?.*$  $firstAddr;
    }
	
	upstream jgyl{
		zone upstreams 64K;
		hash  $clientRealIp;
		server 10.40.29.153:8080;
		server 10.40.29.126:8080;
	}
	
    include       mime.types;
    default_type  application/octet-stream;

    #log_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.log  main;
	
    # 客戶端請求頭緩沖大小
    # nginx 默認會用 client_header_buffer_size 這個 buffer 來讀取 header 值
    # 如果 header 過大,它會使用 large_client_header_buffers 來讀取
    # 如果設置過小的 HTTP 頭,或 Cookie 過大, 會報400 錯誤 nginx 400 bad request
    # 如果超過 buffer,就會報 HTTP 414 錯誤 (URI Too Long)
    # nginx 接受最長的 HTTP 頭部大小必須比其中一個 buffer 大
    # 否則就會報 400 的 HTTP 錯誤 (Bad Request)
    client_header_buffer_size  32k;
    large_client_header_buffers  4  32k;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  90;

    # gzip模塊設置,使用 gzip 壓縮可以降低網站帶寬消耗,同時提升訪問速度。
    # 開啟gzip
    gzip  on;
    # 最小壓縮大小
    gzip_min_length  1k;
    # 壓縮緩沖區
    gzip_buffers  4 16k;
    # 壓縮版本
    gzip_http_version  1.0;
    # 壓縮等級
    gzip_comp_level  2;
    # 壓縮類型
    gzip_types   text/plain text/css text/xml text/javascript application/json application/x-javascript application/xml application/xml+rss;

    server {
        listen       8001;
        server_name  17.167.18.18;
		
		fastcgi_connect_timeout 75;  #鏈接
        fastcgi_read_timeout 90;   #讀取
        fastcgi_send_timeout 100;   #發請求
		
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
		proxy_set_header Host $host:$server_port; # 傳 header 參數至后端服務
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 設置request header 即客戶端IP地址
		proxy_connect_timeout 60s;
		proxy_send_timeout 1800;
		proxy_read_timeout 1800;
		client_max_body_size 100m;
		# 緩沖區代理緩沖用戶端請求的最大字節數
		client_body_buffer_size  128k;
		# 設置代理服務器(nginx)保存用戶頭信息的緩沖區大小
		proxy_buffer_size  4k;
		# proxy_buffers緩沖區,網頁平均在32k以下的話,這樣設置
		proxy_buffers  4  32k;
		# 高負荷下緩沖大小(proxy_buffers*2)
		proxy_busy_buffers_size  64k; 
		# 設定緩存文件夾大小,大於這個值,將從upstream服務器傳
		proxy_temp_file_write_size  64k;
			
		location /jgyl {
			root   html;
			index  index.html index.htm;
			proxy_pass   http://jgyl/jgyl;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM