一般來說,我們在java中都通過如下代碼進行用戶登錄后的服務端注冊,並且在用戶下次請求時無需再登陸一遍,這就是Servlet的Session。使用了這種Session策略,那么Web容器比如tomcat就為當前用戶生成一個SessionID,並且以這個SessionID為索引,存儲這個用戶相關的鍵值對,比如用戶名,登陸時間一類的。存儲在服務器的內存中。同時再response里向用戶瀏覽器中設置一個cookie,這個cookie的名字為jsessionid,內容為服務器生成的隨機數SessionID。在用戶第二次請求時,將這個cookie發給服務器,服務器根據這個SessionID到內存中尋找相關數據,把用戶名什么的提取出來,服務器就可以在本來無狀態的HTTP連接中識別出這是哪個客戶發出的請求,然后繪制相關頁面。
這中Session機制使用簡單方便,被使用了很長時間。但是一旦做成集群,這種方式就不靈了。以NGINX默認的輪詢方式為例,用戶在A服務器上登陸成功,SessionID和用戶名等相關信息寫入了A服務器的內存中,該用戶第二次請求時被NGINX分發到了B服務器,而B服務器沒用該用戶的SessionID和用戶名等相關信息,於是要求用戶再登陸一遍。用戶第二次登陸之后發送第三次請求,被NGINX分配到了A或者C服務器,於是用戶又必須登陸一遍,總之這個用戶一直沒法登陸成功。
基於以上現象,有幾種存儲session 的方法,如下:
1. 數據庫存儲session
2.使用 memcached,redis等存儲session
3. 使用nginx 內置模塊,ip_hash
4. 使用cookie的HASH來區分同一個用戶的不同鏈接
前面我們已經知道了如果使用Servlet Session的話,Web容器會自動的在用戶瀏覽器上建立名為jsessionid的cookie,並且值就是服務器端的SessionID。另一方面,新版的NGINX不光可以通過IP的hash來分發流量,也可以通過url的hash,cookie的hash,header的hash等等進行鏈接的固定分配。由於用戶登陸成功以后名為jsessionid的cookie就有了一個短期固定的值,而且每個用戶都不一樣,那么我們就可以根據這個sessionid的hash值為它分配一個服務器。在當前sessionID起作用的時候那么分配的服務器也是同一個,並且不需要安裝第三方的插件。
本次使用第4種方法,使用cookie 是用戶分配的服務器是同一個。nginx 配置如下:
[root@www vhosts]# cat load.conf
upstream tomcat_server {
hash $cookie_jsessionid;
# hash $request_uri;
server 172.16.31.16:8080;
server 172.16.31.17:8080;
}
#error_page配置備份:
upstream backup {
server 11.11.11.14:80; }
error_page 404 500 502 503 504 =200 @fetch; #注意:=200里的等號,左邊有空格,右邊沒空格
server {
listen 80;
server_name www.load.com;
root /data/wwwroot/www.load.com;
index index.html index.htm index.php;
access_log /usr/local/nginx/logs/www.load.com-access.log gufan;
error_log /usr/local/nginx/logs/www.load.com-error.log;
location /stat {
proxy_pass
http://tomcat_server;
proxy_set_header
Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_intercept_errors on;
proxy_next_upstream off;
}
location @fetch {
proxy_pass http://backup;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}