nginx+php負載均衡叢集環境中的session共享方案梳理

散盡浮華發表於2016-11-04

 

在網站使用nginx+php做負載均衡情況下,同一個IP訪問同一個頁面會被分配到不同的伺服器上,如果session不同步的話,就會出現很多問題,比如說最常見的登入狀態。

下面羅列幾種nginx負載均衡中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)中
PHP可以配置將session儲存在資料庫中,這種方法是把存放session的表和其他資料庫表放在一起,如果mysql也做了叢集的話,每個mysql節點都要有這張表,並且這張session表資料表要實時同步。
但是要注意的是:
用資料庫來同步session,會加大資料庫的IO,增加資料庫的負擔。而且資料庫讀寫速度較慢,不利於session的適時同步。

3)session存在memcache或者redis中
memcache可以做分散式,php配置檔案中設定儲存方式為memcache,這樣php自己會建立一個session叢集,將session資料儲存在memcache中。
特別說明:
以這種方式來同步session,不會加大資料庫的負擔,並且安全性比用cookie大大的提高,把session放到記憶體裡面,比從檔案中讀取要快很多。但是memcache把記憶體分成很多種規格的儲存塊,有塊就有大小,這種方式也就決定了,memcache不能完全利用記憶體,會產生記憶體碎片,如果儲存塊不足,還會產生記憶體溢位。

4)採用nginx中的ip_hash機制
nginx中的ip_hash技術能夠將某個ip的請求定向到同一臺後端web機器中,這樣一來這個ip下的某個客戶端和某個後端web機器就能建立起穩固的session。
也就是說,ip_hash機制能夠讓某一客戶機在相當長的一段時間內只訪問固定的後端的某臺真實的Web伺服器,這樣會話就會得以保持,我們在網站頁面進行login的時候,
就不會在後面的web伺服器之間跳來跳去了,自然也不會出現登陸一次後網站又提醒你沒有登陸需要重新登陸的情況;

ip_hash是在upstream配置中定義的:

upstream nginx.example.com { 
   server 192.168.74.235:80; 
   server 192.168.74.236:80; 
   ip_hash; 
} 
server { 
   listen 80; 
   location / { 
      proxy_pass 
      http://nginx.example.com; 
    } 
}

ip_hash是容易理解的,但是因為僅僅能用ip這個因子來分配後端web,因此ip_hash是有缺陷的,不能在一些情況下使用:
a)nginx不是最前端的伺服器。
ip_hash要求nginx一定是最前端的伺服器,否則nginx得不到正確ip,就不能根據ip作hash。譬如使用的是squid為最前端,那麼nginx取ip時只能得到squid的伺服器ip地址,用這個地址來作分流是肯定錯亂的。
b)nginx的後端還有其它方式的負載均衡。
假如nginx後端又有其它負載均衡,將請求又通過另外的方式分流了,那麼某個客戶端的請求肯定不能定位到同一臺session應用伺服器上。這麼算起來,nginx後端只能直接指向應用伺服器,或者再搭一個squid,然後指向應用伺服器。最好的辦法是用 location作一次分流,將需要session的部分請求通過ip_hash分流,剩下的走其它後端去。

----------------------------順便說一下之前線上用過的nginx負載均衡中的session共享處理方案----------------------------
用的就是上面第三站方式,將session存放在memcached裡面。

公司的一些網站頁面(LNMP框架)涉及到登陸需求(有sessionID),用到了memcache快取服務,將php的sessionID快取到memcache裡面。
將sessionID放在memcache裡後,會加快頁面訪問速度,頁面訪問飛快!

如果memcache裡面存放的只是sessionID,而沒有其他業務,那麼memcache伺服器的記憶體消耗就不大!

首先保障php擴充套件模組裡要支援memcached功能(即一定要安裝php的memcached擴充套件模組)
[root@huanqiu vhosts]# /Data/app/php5.5.1/bin/php -m
[PHP Modules]
..........
memcached
...........

遇到問題:
在遷移網站業務的過程中(遷移後使用的是新的memcache機器)
由於php.ini和程式碼中的memcache連線資訊沒有及時修改或者沒有完全修改過來,導致遷移後的頁面訪問速度有點緩慢,有點卡!
最後仔細排查,把所有有關memcache連線資訊的配置都改過來了,遷移後的頁面訪問速度就正常了!

1)首先部署三臺memcache伺服器,主機名分別是memcache1.server ,memcache2.server ,memcache3.server ,啟動相應的埠。
注意,不用業務應用到的memcache服務埠不能衝突。
比如:業務A用到memcache1-3.server伺服器的11021,11022,11023埠,業務B就用到了memcache1-3.server的11031,11032,11033埠

部署memcache叢集服務
yum安裝即可,部署三臺memcache1,memcache2,memcache3
啟動相應快取埠
[root@memcache2 ~]# ps -ef|grep memcache
root      6139     1  0 May30 ?        00:00:05 /usr/bin/memcached -d -m 512 -p 11021 -u root -c 4096 -P  /var/lib/memcache/logs/memcached_11021.pid
root      6184     1  0 May30 ?        00:00:05 /usr/bin/memcached -d -m 512 -p 11022  -u root -c 4096 -P /var/lib/memcache/logs/memcached_11022.pid
root      6198     1  0 May30 ?        00:00:05 /usr/bin/memcached -d -m 512 -p 11023 -u root -c 4096 -P  /var/lib/memcache/logs/memcached_11023.pid
root      6214     1  0 May30 ?        00:00:05 /usr/bin/memcached -d -m 512 -p 11031 -u root -c 4096 -P  /var/lib/memcache/logs/memcached_11031.pid
root      6229     1  0 May30 ?        00:00:05 /usr/bin/memcached -d -m 512 -p 11032 -u root -c 4096 -P  /var/lib/memcache/logs/memcached_11032.pid
root      6244     1  0 May30 ?        00:00:05 /usr/bin/memcached -d -m 512 -p 11033 -u root -c 4096 -P  /var/lib/memcache/logs/memcached_11033.pid

將上面的程式新增到開機啟動/etc/rc.local裡面

2)在業務機器上應用memcache快取
     a)比如業務A
     首先在相應的業務伺服器上的/etc/hosts裡設定主機對映(如果能ping通memcache機器的內網,就用內網)
     #vim /etc/hosts
      192.168.1.23  memcache1.server  
      192.168.1.24  memcache2.server
      192.168.1.25  memcache3 .server  

     首先在php的php.ini裡面設定memcache快取
    #vim /Data/app/php/etc/php.ini
       ...............
       [Session]
       session.save_handler = memcached
       session.save_path = "memcache1.server :11021,memcache2.server :11022,memcache3.server :11023"
    
     然後重啟php服務
 
     最後在相應的程式碼程式裡使用memcache快取,比如:
     # vim  main.php
     $config['params']['erp_host']   = 'http://www.xqshijie.com';
                //以下是memcache配置,把相應的引數都換成相應環境下的
                        $config['components']['cache']['class'] = 'system.caching.CMemCache';
                        $config['components']['cache']['useMemcached'] = 'true';
                        $config['components']['cache']['keyPrefix'] = '';
                        $config['components']['cache']['hashKey'] = false;
                        $config['components']['cache']['serializer'] = false;
                        $config['components']['cache']['servers'][0]['host'] = 'memcache1.server';
                        $config['components']['cache']['servers'][0]['port'] = 11021;
                        $config['components']['cache']['servers'][0]['weight'] = 10;
...........................


     b)業務B
     在相應業務伺服器的/etc/hosts裡設定主機對映(如果能ping通memcache機器的內網,就用內網)
     #vim /etc/hosts
      192.168.1.23  memcache1.server  
      192.168.1.24  memcache2.server
      192.168.1.25  memcache3 .server  

     首先在php的php.ini裡面設定memcache快取
    #vim /Data/app/php/etc/php.ini
       ...............
       [Session]
       session.save_handler = memcached
       session.save_path = "memcache1.server :11031,memcache2.server :11032,memcache3.server :11033"
    
     然後重啟php服務
 
     最後在相應的程式碼程式裡使用memcache快取,比如:
     # vim  main.php
     $config['params']['erp_host']   = 'http://erp.fangfull.com';
                //以下是memcache配置,把相應的引數都換成相應環境下的
                        $config['components']['cache']['class'] = 'system.caching.CMemCache';
                        $config['components']['cache']['useMemcached'] = 'true';
                        $config['components']['cache']['keyPrefix'] = '';
                        $config['components']['cache']['hashKey'] = false;
                        $config['components']['cache']['serializer'] = false;
                        $config['components']['cache']['servers'][0]['host'] = 'memcache1.server';
                        $config['components']['cache']['servers'][0]['port'] = 11031;
                        $config['components']['cache']['servers'][0]['weight'] = 10;
.............................................

--------------------------------------------------------------------------------------------------
清理memcache快取的方法:
1)
預設memcache會監聽11221埠,如果想清空伺服器上memecache的快取,大家一般使用的是:
telnet localhost 11211
flush_all

2)同樣也可以使用:
echo "flush_all" | nc localhost 11211
使用flush_all 後並不是刪除memcache上的key,而是置為過期

------------------------------------php.ini中關於session屬性的相關設定-------------------------------------
1)
session.use_cookies:是否在客戶端用 cookie 來存放會話 ID,1是開啟 ,0是關閉
若session.use_cookies = 1
sessionid在客戶端採用的儲存方式,置1代表使用cookie記錄客戶端的sessionid,同時,$_COOKIE變數裡才會有$_COOKIE[‘PHPSESSIONID’]這個元素存在

一般指令碼語言都會原生支援“session機制”,如PHP程式配置:
設定php.ini的session.use_trans_sid = 1,PHP自動在URL裡傳遞session id
設定php.ini的session.use_cookies = 1,使用cookie在客戶端儲存session id

2)
session.auto start:
將php.ini中的如下選項配置修改即可:
session.auto_start=0
修改成
sessioin.auto_start=1
開啟session.auto_start
優點在於,任何時候都不會因忘記執行session_start()或session_start()在程式裡的位置不對,而導致錯誤;
缺點在於,如果你使用的是第三方程式碼,則必須刪去其中的全部 session_start(),否則將不能得到正確的結果。

3)
session的內容存在檔案裡的話,檔案在哪兒?
如果不指定, Linux下預設在 "/tmp"目錄。
線上在php.ini配置檔案了做了指定,session內容存放在memcache快取裡。
預設session內容是儲存在檔案裡的,即session.save_handler = files
但是我們線上是設定將session內容儲存到memcache裡的

線上環境下的配置:
[Session]
; Handler used to store/retrieve data.
; http://php.net/session.save-handler
;session.save_handler = files
session.save_handler = memcached
session.save_path = "memcache1.huanqiu.com:11311,memcache1.huanqiu.com:11312,memcache2.huanqiu.com:11311,memcache2.huanqiu.com:11312"

4)
session的生命週期的設定
a)session的預設生命週期是多久?
答:關閉瀏覽器就失效
原因:因為session_id存在於cookie,而預設情況,cookie關閉瀏覽器即失敗.
b)如何設定session生命週期為30分鐘呢?
在php.ini檔案裡設定session.cookie_lifetime = 1800

線上生產環境下設定的是7天,生命週期是一週
; Lifetime in seconds of cookie or, if 0, until browser is restarted.
; http://php.net/session.cookie-lifetime
session.cookie_lifetime = 604800

5)
session的名字
; Name of the session (used as cookie name).
; http://php.net/session.name
session.name = PHPSESSID

------------------------------------------session與cookie的簡單區別-----------------------------------------
session和cookie本質上確實是兩個東西,但cookie同時也是session id的載體,cookie儲存session id。
1)cookie資料存放在客戶的瀏覽器上,session資料放在伺服器上。
session儲存在伺服器端與瀏覽器設定無關,cookie在客戶端並受瀏覽器設定限制。
cookie是在你的電腦上儲存的,session是在伺服器上的. 也就是說你換一個電腦你的cookie就不起作用了, 而session只要你的瀏覽器不關就還能訪問到. 通常的都是兩者結合著用的. cookie的話你自己就可以通過對瀏覽器的設定禁用掉.這樣就不起作用了

2)cookie不是很安全,別人可以分析存放在本地的cookie並進行cookie欺騙,考慮到安全應當使用session。
session是伺服器端快取,cookie是客戶端快取。
cookie機制採用的是在客戶端保持狀態的方案,而session機制採用的是在伺服器端保持狀態的方案

3)session會在一定時間內儲存在伺服器上。當訪問增多,會比較佔用你伺服器的效能,考慮到減輕伺服器效能方面,應當使用cookie。
session是伺服器保持客戶端狀態資訊的方案,一般是儲存在伺服器中的一塊記憶體中,session超時時間在伺服器端進行設定。
cookie是客戶端保持使用者資訊的方案,一般是檔案形式儲存,cookie清空時間是在客戶端瀏覽器設定。
從開發角度說,session資訊可以通過技術方案寫到客戶端儲存,cookie中的使用者資訊,也可以在使用者訪問該網站時,通過技術手段自動更新使用者的session資訊。

4)單個cookie儲存的資料不能超過4K,很多瀏覽器都限制一個站點最多儲存20個cookie。

5)建議:將登陸資訊等重要資訊存放為session;其他資訊如果需要保留,可以放在cookie中

-------------------------------------------開啟session功能----------------------------------------
開啟session功能是很重要的,比如下面一個場景:某個網站程式在測試伺服器上除錯,首頁是ok的,但一到後臺去登入就登入不進去,起初懷疑是rewrite規則沒有寫對,後排查就是因為session功能沒有開啟引起的!
那麼session應該如何開啟?
1)編輯php.ini配置檔案
session.save_path=資料夾路徑      指向任意一個有寫許可權的目錄就行了.
register_globals = On           開啟全域性變數,如果不開啟,你就這樣用$_SESSION['sessioname'];但是我本人從來沒成功過.
2)重啟php服務即可(如果是lamp模式,就重啟apache)

-----------------------------------------看一個linux下Session丟失的案例分析----------------------------------------
由於各種原因需要進行程式碼遷移,遷移後重新搭建php環境,執行程式碼。最後在登入頁面時發現後臺不能訪問,會直接返回到登入頁面,接著對程式碼進行測試,沒有報任何錯誤,最後排查是因為跳轉時session丟失造成的!那麼session如何會丟失呢?
發現造成這個原因有這幾種:
a)session儲存路徑(目錄)不存在,自然就無法生成session臨時檔案
b)session儲存路徑下有沒有許可權,如果沒有,也就不可能儲存session資料
c)能正常存session資料,但session存入後被清空

嘗試解決的措施:
a)在專案根目錄下建立phpinfo.php檔案,在檔案中寫入phpinfo(),執行此檔案,檢視頁面,就可以找到session的儲存路徑,
b)在伺服器上查詢session儲存路徑是否存在,不存在建立儲存目錄,並分配許可權,如果有session儲存路徑,就檢視其是否有許可權,沒有就分配許可權,
c)是否是第三個原因,可在phpinfo.php頁面中查詢date.timezone是否設定不對,然後在php.ini配置檔案中找到date.timezone進行配置

-------------------------------------------------------------------------------------------------------------------
需要清楚知道的:
1)上面在php.ini檔案裡將session.save_handler修改為memcached,即表示將php的session資訊存放到memcache裡(前提是安裝了memcached擴充套件),然後在session.save_path處配置連線memcache資訊。如:
session.save_handler = memcached
session.save_path = "memcache1.huanqiu.com:11311,memcache1.huanqiu.com:11312,memcache2.huanqiu.com:11311,memcache2.huanqiu.com:11312"

注意:
帶d的memcached擴充套件,則session.save_path配置連線的時候不需要加tcp://
如果是不帶d的memcache擴充套件,則session.save_path配置連線的時候需要加tcp://

2)如果將session.save_handler修改為redis,即表示將php的session資訊存放到redis裡(前提是安裝了php的phpredis擴充套件),然後在session.save_path處配置redis的connect 地址。如下:
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379"
-------------------------------------------------------------------------------------------------------------------

相關文章