在日常運維工作中,當給Web站點使用負載均衡之後,必須面臨的一個重要問題就是Session的處理辦法,無論是PHP、Python、Ruby還是Java語言環境,只要使用伺服器儲存Session,在做負載均衡時都需要考慮Session的問題。
通常面臨的問題
從使用者端來解釋,就是當一個使用者第一次訪問被負載均衡代理到後端伺服器A並登入後,伺服器A上保留了使用者的登入資訊;當使用者再次傳送請求時, 根據負載均衡策略可能被代理到後端不同的伺服器,例如伺服器B,由於這臺伺服器B沒有使用者的登入資訊,所以導致使用者需要重新登入。這對使用者 來說是不可忍受的。所以,在實施負載均衡的時候,我們必須考慮Session的問題。 在負載均衡中,針對Session的處理,一般有以下幾種方法: 1)Session會話保持(案例:Nginx、Haproxy) 2)Session會話複製(案例:Tomcat) 3)Session會話共享(案例:Memcached、Redis)
一、Session會話保持
Session保持(會話保持)是我們見到最多的名詞之一,通過會話保持,負載均衡進行請求分發的時候保證每個客戶端固定的訪問到後端的同一臺應用伺服器。 會話保持方案在所有的負載均衡都有對應的實現。而且這是在負載均衡這一層就可以解決Session問題。 ================Nginx 做負載均衡的Session保持================ 對於Nginx可以選用Session保持的方法實行負載均衡,nginx的upstream目前支援5種方式的分配方式,其中有兩種比較通用的Session解決方法,ip_hash和url_hash。 注意:後者不是官方模組,需要額外安裝。 ip_hash 每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個後端伺服器,達到了Session保持的方法。 例: upstream bakend { ip_hash; server192.168.0.11:80; server192.168.0.12:80; } ================Haproxy做負載均衡的Session保持================ Haproxy作為一個優秀的反向代理和負載均衡軟體,也提供了多種Session保持的方法,下面列舉了兩種最常用的: 1) 源地址 Hash haroxy 將使用者IP經過hash計算後指定到固定的真實伺服器上(類似於nginx 的ip hash 指令) 配置指令:balancesource 2)使用cookie 進行識別 也就是Haproxy在使用者第一次訪問的後在使用者瀏覽器插入了一個Cookie,使用者下一次訪問的時候瀏覽器就會帶上這個Cookie給Haproxy,Haproxy進行識別。 配置指令:cookie SESSION_COOKIE insert indirect nocache 配置例子如下: cookie SERVERID insert indirect nocache server web01 192.168.56.11:8080 check cookie web01 server web02 192.168.56.12:8080 check cookie web02 =========================================================== 會話保持的缺點: 1) 會話保持看似解決了Session同步的問題,但是卻帶來的一些其它方面的問題: 2)負載不均衡了:由於使用了Session保持,很顯然就無法保證負載絕對的均衡。 3)沒有徹底解決問題:如果後端有伺服器當機,那麼這臺伺服器的Session丟失,被分配到這臺服務請求的使用者還是需要重新登入。
二、Session會話保持
既然,我們的目標是所有伺服器上都要保持使用者的Session,那麼將每個應用伺服器中的Session資訊複製到其它伺服器節點上是不是就可以呢? 這就是Session的第二中處理辦法:會話複製。 會話複製在Tomcat上得到了支援,它是基於IP組播(multicast)來完成Session的複製,Tomcat的會話複製分為兩種: 1)全域性會話複製:利用Delta Manager複製會話中的變更資訊到叢集中的所有其他節點。 2)非全域性複製:使用Backup Manager進行復制,它會把Session複製給一個指定的備份節點。 不過,這裡不準備來解釋會話複製的Tomcat配置,如果有需求可以參考Tomcat官方文件,主要是因為會話複製不適合大的叢集。根據生產的實踐案例, 在叢集超過6個節點之後就會出現各種問題,不推薦生產使用。
三、Session會話共享
既然會話保持和會話複製都不完美,那麼我們為什麼不把Session放在一個統一的地方呢,這樣叢集中的所有節點都在一個地方進行Session的存取就可以解決問題。 ========================================================================================= Session存放到哪裡? 對於Session來說,肯定是頻繁使用的,雖然你可以把它存放在資料庫中,但是真正生產環境中我更推薦存放在效能更快的分散式KV資料中, 例如:Memcached和Redis。 --------------------------------------------------------------- PHP設定Session共享 如果使用的是PHP那麼恭喜你,配置非常的簡單。PHP通過兩行配置就可以把Session存放在Memcached或者Redis中,當然你要提前配置好他們。修改php.ini: 使用Memcache儲存Session session.save_handler = memcache session.save_path = "tcp://192.168.56.11:11211" 使用Redis儲存Session session.save_handler = redis session.save_path ="tcp://localhost:6379" 提醒:別忘了給PHP安裝memcache或者redis外掛。 --------------------------------------------------------------- Tomcat設定Session共享 可以使用MSM(Memcached Session Manager)來實現同樣把Session存放到Memcache中。 --------------------------------------------------------------- Django設定Session共享 在Django中Session是通過一箇中介軟體管理的。如果要在應用程式中使用Session,需要在settings.py中的MIDDLEWARE_CLASSES變數中加入 'django.contrib.sessions.middleware.SessionMiddleware' 。Django的Session引擎可以將Session存放在三個地方,分別是:資料庫、快取、檔案。 --------------------------------------------------------------- 如果你想使用資料庫支援的會話,你需要新增’django.contrib.sessions’到你的INSTALLED_APPS設定中。在配置完成之後,請執行manage.py migrate 來安裝儲存會話資料的一張資料庫表。 --------------------------------------------------------------- 使用快取保持Session 對於簡單的快取會話: 可以設定SESSION_ENGINE 為”django.contrib.sessions.backends.cache”。此時會話資料將直接儲存在你的快取中。然而,快取資料將可能不會持久: 如果快取填滿或者快取伺服器重啟,快取資料可能會被清理掉。 若要持久的快取資料: 可以設定SESSION_ENGINE為”django.contrib.sessions.backends.cached_db”。它的寫操作使用快取,對快取的每次寫入都將再寫入到資料庫。對於 讀取的會話,如果資料不在快取中,則從資料庫讀取。兩種會話的儲存都非常快,但是簡單的快取更快,因為它放棄了永續性。大部分情況下,cached_db後端已經足夠快,但是如果你需要榨乾最後一點的效能,並且接受會話資料丟失的風險,那麼你可使用cache而不是cached_db 使用檔案儲存Session 使用檔案儲存Session不再我們的討論之類,因為很難進行共享,PHP預設也是將Session存放在/tmp目錄下。
簡單總結:
- 會話保持的缺點:負載不均衡;沒有徹底解決問題.
- 會話複製的缺點:叢集超過6個節點就會出現一系列的問題.
- 會話共享:會話資料共享在Nosql(Redis)資料庫中分享。