目錄
參考
概要
問題:
由於生產環境需要高可用,所以增加了兩臺服務器做高可用集羣,然後搭建完成之後,出現了一些登錄態失效需要重新登錄的問題。
原因:
定位問題發現是因爲用戶登錄態是保存在Session中的,默認是保存在文件中的,且沒有做共享Session,導致在某一臺登錄成功之後,寫入到Session文件中,然後刷新頁面就有可能請求到另一臺服務器上,而另一臺服務器中的Session文件是沒有該登錄態的,就會導致服務器認爲用戶沒有登錄需要重新登錄
解決方法:
一般業界通用的做法有兩種:
- 使用 IP_HASH 做會話保持(Session保持):每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個後端服務器,達到了Session保持的方法。
- 使用 Redis/Memcache等Nosql來做會話共享(Session共享):把Session放在一個統一的地方,集羣中的所有節點都在這個地方進行Session的存取
Session保持(會話保持)是我們見到最多的名詞之一,通過會話保持,負載均衡進行請求分發的時候保證每個客戶端固定的訪問到後端的同一臺應用服務器。會話保持方案在所有的負載均衡都有對應的實現。而且這是在負載均衡這一層就可以解決Session問題。
會話保持看似解決了Session同步的問題,但是卻帶來的一些其它方面的問題:
- 負載不均衡了:由於使用了Session保持,很顯然就無法保證負載絕對的均衡。
- 沒有徹底解決問題:如果後端有服務器宕機,那麼這臺服務器的Session丟失,被分配到這臺服務請求的用戶還是需要重新登錄。
所以一般呢,更加推薦使用 Redis/Memcache等Nosql來做會話共享(Session共享)
共享Session
這裏只講使用 Memcache來做Session共享
Memcache服務端和客戶端(PHP)的安裝見:【Memcache】Linux和Windows下Memcache服務端和客戶端(PHP)的安裝【原創】
對於PHP來說,實現Session共享,其實就是把Session的存儲,從默認的文件存儲改成使用Memcache來存儲即可,有三種方法:
- 修改php.ini(建議)
- 設置.htaccess
- 代碼
1. 修改php.ini
修改php.ini就是全局設置,修改後所有的Session默認使用Memcache,如果有多個memcached服務端的話,用逗號,隔開
# memcache擴展,如果有多個memcached服務端的話,用逗號,隔開
session.save_handler = memcache
session.save_path = "tcp://127.0.0.1:11211"
# memcached擴展,如果有多個memcached服務端的話,用逗號,隔開
session.save_handler = memcached
session.save_path = "127.0.0.1:11211"
修改後,重啓php-fpm和nginx,然後可以通過查看phpinfo頁面的session查看是否生效
如圖:
注意:如果是memcached的話,那麼圖片裏面就會是memcached了
注意:如果修改後發現phpinfo頁面還是指向默認的文件緩存地址/tmp,說明還有其他的Session配置,在Linux中php-fpm的配置文件:/etc/php-fpm.conf或者/etc/php-fpm.d/*.conf中,而且它們的優先級比php.ini高,會覆蓋php.ini中的配置,所以也需要在這裏面修改:
# memcache擴展,如果有多個memcached服務端的話,用逗號,隔開
php_value[session.save_handler] = memcache
php_value[session.save_path] = "tcp://127.0.0.1:11211"
# memcached擴展,如果有多個memcached服務端的話,用逗號,隔開
php_value[session.save_handler] = memcached
php_value[session.save_path] = "127.0.0.1:11211"
然後重啓php-fpm和nginx即可
2. 設置.htaccess
在某個目錄下,修改.htaccess:
# memcache擴展,如果有多個memcached服務端的話,用逗號,隔開
php_value session.save_handler "memcache"
php_value session.save_path "tcp://127.0.0.1:11211"
# memcached擴展,如果有多個memcached服務端的話,用逗號,隔開
php_value session.save_handler "memcached"
php_value session.save_path "127.0.0.1:11211"
這個方法一般不推薦
3. 代碼
PHP代碼中頂部加上:
<?php
# memcache擴展,如果有多個memcached服務端的話,用逗號,隔開
ini_set("session.save_handler", "memcache");
ini_set("session.save_path", "tcp://127.0.0.1:11211");
# memcached擴展,如果有多個memcached服務端的話,用逗號,隔開
ini_set("session.save_handler", "memcached");
ini_set("session.save_path", "127.0.0.1:11211");
這個是隻影響到該文件,一般也不會這麼用
測試
寫一個PHP文件:
注意:以下測試的代碼是針對Memcache擴展的,如果是Memcached擴展的話,實例化類的時候需要使用$mem = new Memcached;
<?php
session_start();
var_dump(111);
$_SESSION['test_ddd'] = 'Hello World';
$session_id = session_id();
echo $session_id . PHP_EOL;
var_dump($_SESSION);
$mem = new Memcache;
if(!$mem->addServer('127.0.0.1', 11211)){
var_dump('連接失敗!');
}
var_dump($mem->get($session_id));
然後在瀏覽器訪問,然後再註釋掉$_SESSION['test_ddd'] = 'Hello World';,再去訪問,看看能不能訪問到即可
也可以使用telnet來查看memcached中是否有數據:
[root@www html]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get memc.sess.key.df07ae825edf170a4d2d1b417ddf445d
VALUE memc.sess.key.df07ae825edf170a4d2d1b417ddf445d 0 28
test_ddd|s:11:"Hello World";
END
注意:get memc.sess.key.df07ae825edf170a4d2d1b417ddf445d,前面的memc.sess.key是Memcached默認的前綴,後面跟着的是session_id的值
注意:session的前綴,可以通過phpinfo頁面查看: