一、場景
當nginx做了負載均衡之后,同一個ip的url請求服務器的時候,負載均衡會根據每台服務器的權重等一些設置將請求轉發到不同的服務器上去進行處理,這樣的話針對一些帶有狀態請求的情況來說就是個很大的問題,因為是帶有狀態的請求就好比登陸狀態一樣,A用戶登陸系統,負載均衡機制把A用戶的登陸請求分發給了s1服務器,這個時候s1服務器上就會記錄A用戶登陸的session信息,登陸成功后,當A用戶進行相應的操作,比如進入個人中心,這時候這個請求經過反向代理服務器的時候,負載均衡機制根據當前集群中的各個服務器的壓力性能等情況可能把請求分發給了s2服務器處理,那么這個時候會去驗證用戶的狀態是否登錄,也就是驗證session,可是A用戶的session保存在了s1服務器上,造成再s2服務器上請求驗證狀態找不到對應的session,就會認為用戶未登錄而做的異常操作,就會提醒用戶去登錄,從而跳轉到登錄頁面,那么這就是負載均衡針對帶有狀態的請求的一個弊端。
二、解決的方案
1. 不使用session,使用cookie
session是存放在服務器端的,cookie是存放在客戶端的,我們可以把用戶訪問頁面產生的session放到cookie里面,就是以cookie為中轉站。你訪問web服務器A,產生了session然后把它放到cookie里面,當你的請求被分配到B服務器時,服務器B先判斷服務器有沒有這個session,如果沒有,再去看看客戶端的cookie里面有沒有這個session,如果也沒有,說明session真的不存,如果cookie里面有,就把cookie里面的sessoin同步到服務器B,這樣就可以實現session的同步了。
說明:這種方法實現起來簡單,方便,也不會加大數據庫的負擔,但是如果客戶端把cookie禁掉了的話,那么session就無從同步了,這樣會給網站帶來損失;cookie的安全性不高,雖然它已經加了密,但是還是可以偽造的,所以這種方式也是不推薦的。
2. session存在數據庫mysql
session保存在數據庫中,是把session表和其他的數據表存放在一起,那么當用戶只要登錄后隨便操作了些什么就要去數據庫驗證一下session的狀態,這樣無疑加重了mysql數據庫的壓力;如果數據庫也做了集群的話,那么也就是說每個數據庫集群的節點都得保存這個session表,而且要保證每個集群的節點中數據庫的session表的數據保持一致,實時同步
說明:session保持在數據庫,加重了數據庫的IO,增大數據庫的壓力和負擔,從而影響數據庫的讀寫性能,而且mysql集群的話也不利於session的實時同步
3. session存在緩存memcache或者redis中
memcache可以做分布式,php配置文件中設置存儲方式為memcache,這樣php自己會建立一個session集群,將session數據存儲在memcache中。
說明:這種方式來同步session,不會加大數據庫的負擔,而且安全性比用cookie保存session大大的提高,把session放到內存里面,比從文件中讀取要快很多。但是memcache把內存分成很多種規格的存儲塊,有塊就有大小,這種方式也就決定了,memcache不能完全利用內存,會產生內存碎片,如果存儲塊不足,還會產生內存溢出。
4. ip_hash技術,nginx中可以配置,當某個ip下的客戶端請求指定(固定,因為根據IP地址計算出一個hash值,根據hash值來判斷分配給那台服務器,從而每次該ip請求都分配到指定的服務器)的服務器,這樣就可以保證有狀態請求的狀態的完整性,不至於出現狀態丟失的情況,一下是nginx的配置,可以參考一下:
upstream nginx.example.com { server 192.168.1.2:80; server 192.168.1.3:80; ip_hash; } server { listen 80; location / { proxy_pass http://nginx.example.com; } }
注意:ip_hash這個方案確實可以保證帶有狀態的請求的完整性,但是它有一個很大的缺陷,那就是ip_hash方案必須保證Nginx是最前端的服務器(接受真實的ip),如果nginx不是最前端的服務器,還存在中間件(中間服務器什么的),那么nginx獲取的ip地址就不是真實的ip地址,那么這個ip_hash就沒有任何意義
5.還有其他的一些方法,本人暫時還不太清楚,有待繼續的學習(url_hash不知道可以不可以?是否添加一台共享數據的服務器,把狀態等一些公共的數據都保持到這台服務器上,從而集群的所有服務器都從共享服務器上邊獲取狀態進行驗證??待求證)
內容出自網上各位兄弟姐妹的好文章,萬分感謝!!!