PHP是如何實現多伺服器SESSION共享

語言: CN / TW / HK

點選進入“PHP開源社群”    

免費獲取進階面試、文件、影片資源

為什麼要session共享

現在稍微大一點的網站基本上都有好幾個子域名,比如www.feiniu.com, search.feiniu.com, member.feiniu.com,這些網站如果需要共用使用者登入資訊,那麼就需要做到session共享,當然前提是有相同的主域。

PHP的session原理

客戶端訪問php頁面,執行session_start,生成session_id,一般我們是把session_id儲存到cookie上,session內容儲存在服務端,客戶端訪問訪問不同的頁面都會把session_id傳到服務端,通過session_id來獲取session內容。

流程是這樣,可是不同的伺服器會對同一個客戶端產生不同的session_id,這樣的話不同伺服器就不能得到相同的session內容了。而且PHP 預設的 SESSION 資料都是分別儲存在本伺服器的檔案系統中。

所以我們要解決session共享,就必須解決 兩個問題

1. 多臺伺服器用同一個session_id

這個比較容易解決,只要在php中設定存session_id的cookie域名
為網站主域就可以了
開啟PHP.ini, 設定session.cookie_domain = .feiniu.com,
當然也可以在php程式碼當中
設定ini_set("session.cookie_domain","feiniu.com");

2. 多臺伺服器用同一個session_id訪問到相同的session內容

要實現這點,就必須把session內容儲存到讓所有伺服器都能訪問到的地方,

php的session內容是預設儲存到本伺服器的檔案中的,

一般的解決方案是存入資料庫,memcache或者redis這種快取伺服器,

當然用預設的檔案儲存方式也可以,用NFS統一儲存。

3. 如何選擇儲存引擎

預設檔案儲存: 這種方式的session銷燬依託於php垃圾收集器,在高併發或銷燬時間較長的情況下,在SESSION目錄下產生大量檔案,當然可以設定分級目錄進行 SESSION 檔案的儲存。

這會導致兩個問題:第一、查詢檔案慢;第二,每個目錄下可容納的檔案數是有限的,可能會導致新SESSION儲存失敗。

1) 設定Session儲存的引擎,php.ini 檔案

[Session]

session.save_handler = files
session.save_path = /data/SessionLogs

2)預設情況下的Session的使用 

<?php
/**
* session的使用
* 預設情況(不更改session.save_handle引數時),是儲存在檔案file中的
* 預設情況下使用session的情況(使用者24分鐘內沒有重新整理操作會過期)
* 每個使用者對應唯一session_id,每一個session_id對應伺服器中儲存的一個session檔案,這個檔案中儲存了當前session_id的資訊,比如下面,就儲存了name和age的鍵值
*/

session_start();
echo session_id();
echo "<br>";
$_SESSION['age'] = 26;
$_SESSION['name'] = 'xiaobudiu';
var_dump($_SESSION);

3)在伺服器中儲存的形式是這樣的

資料庫儲存: 把Session儲存在資料庫裡可以防止Session資料被垃圾收集器刪除,可以固化儲存session資料。但是用資料庫來同步session,會加大資料庫的IO,增加資料庫的負擔。而且資料庫讀寫速度較慢,不利於session的適時同步。

memcache儲存:

以這種方式來同步session,不會加大資料庫的負擔,並且安全性比較高,把session放到記憶體裡面,比從檔案中讀取要快很多。

但是memcache把記憶體分成很多種規格的儲存塊,有塊就有大小,這種方式也就決定了,memcache不能完全利用記憶體,會產生記憶體碎片,如果儲存塊不足,還會產生記憶體溢位

那些不需要“分佈”的,不需要共享的,或者乾脆規模小到只有一臺伺服器的應用,memcached不會帶來任何好處,相反還會拖慢系統效率,因為網路連線同樣需要資源。

Redis儲存:

與memcache相比,redis訪問稍稍慢一點點,好處是: redis支援的資料結構較多,可以儲存陣列或物件,而memcache只能儲存字串。

在session機器重啟的情況下,memcache所有使用者都必須重新獲得 session,而redis不會 在突然湧來大量使用者產生了很多資料把儲存 session 的機器記憶體佔滿了的情況下,memcache 會罷工,所有 key 都沒過期的話就不停的覆蓋最後寫入的資料,而 redis 只是會變慢 ,不會影響程式的邏輯。

1) 設定php.ini 檔案中的session.save_handle 和session.save_path

session.save_handler = Redis
session.save_path = "tcp://localhost:6379"

注1:如果連線的是遠端redis,需要將localhost換成對應的遠端ip地址。像這樣

session.save_handler = Redis
session.save_path = "tcp://47.94.203.119:6379"

注2:如果為redis已經添加了auth許可權(requirpass),session.save_path項則應該這樣寫

session.save_handler = Redis
session.save_path = "tcp://47.94.203.119:6379?persistent=1&database=10&auth=myredisG506"

2)使用redis儲存session資訊

<?php
/**
* 將session儲存在redis中
*/

session_start();
echo session_id();
echo "<br>";
$_SESSION['age'] = 26;
$_SESSION['name'] = 'xiaobudiu';
$_SESSION['sex'] = 'man';
var_dump($_SESSION);

在redis上是以這樣的形式進行儲存的

使用Redis儲存Session,並設定Session會話存活時間以及Session中某一元素存活時間

封裝session類 b.php

<?php
/**
* session控制類
*
*/

class Session
{
function __construct($lifetime = 3600)
{
//初始化設定session會話存活時間
ini_set('session.gc_maxlifetime',$lifetime);
}
/**
* 設定當前會話session的key-value
* @param String $name session name
* @param Mixed $data session data
* @param Int $expire 有效時間(秒)
*/

function set($name, $data, $expire = 600)
{
$session_data = array();
$session_data['data'] = $data;
$session_data['expire'] = time()+$expire;
$_SESSION[$name] = $session_data;
}
/**
* 讀取當前會話session中的key-value
* @param String $name session name
* @return Mixed
*/

function get($name)
{
if(isset($_SESSION[$name])) {
if($_SESSION[$name]['expire'] > time()) {
return $_SESSION[$name]['data'];
}else{
self::clear($name);
}
}
return false;
}

/**
* 清除當前session會話中的某一key-value
* @param String $name session name
*/

function clear($name)
{
unset($_SESSION[$name]);
}

/**
* 刪除當前session_id對應的session檔案(清空當前session會話儲存)
*/

function destroy()
{
session_destroy();
}

}

session類的使用:d.php

<?php
require_once 'b.php';
session_start();
$session = new Session();
$session->set('wan','kkkk',1966);

$session->set('name','xiaobudiu');
$session->set('age',26);
$session->set('sex','man');


//輸出當前會話的session儲存資料
var_dump($_SESSION);



//unset掉某一個session屬性
//$session->clear('name');

//刪除當前session_id對應session檔案
//$session->destroy();
//echo $session->get('sex');

redis中顯示:

*宣告:本文於網路整理,版權歸原作者所有,如來源資訊有誤或侵犯權益,請聯絡我們刪除或授權事宜

END

PHP開源社群

掃描關注  進入”PHP資料“

免費獲取進階

面試、文件、影片資源

點選“檢視原文”獲取更多