web服務器有多臺,每臺服務器都會存貯本身的session,session沒法在多臺服務器共享。因此就須要更換session的存貯空間,存貯在一個共用的空間。一般爲了讀寫速度,咱們會選擇存貯在內存服務上,如redis、mysql的memory存貯引擎等,本文以reddis存貯貫串上下文。php
... upstream backserver { ip_hash; server 192.168.0.14:88; server 192.168.0.15:80; } ...
採用ip_hash指令解決這個問題,若是客戶已經訪問了某個服務器,當用戶再次訪問時,會將該請求經過哈希算法,自動定位到該服務器。
每一個請求按訪問ip的hash結果分配,這樣每一個訪客固定訪問一個後端服務器,解決session的問題。mysql
按訪問url的hash結果來分配請求,使每一個url定向到同一個(對應的)後端服務器,後端服務器爲緩存時比較有效。nginx
... upstream backserver { server squid1:3128; server squid2:3128; hash $request_uri; hash_method crc32; } ...
session.save_handler = redis session.save_path = "tcp://127.0.0.1:6379"
修改完以後,重啓 php-fpm。git
ini_set("session.save_handler", "redis"); ini_set("session.save_path", "tcp://127.0.0.1:6379");
主流框架都選擇用此類方法覆蓋session的open、close、read、write、gc機制,容易擴展。我寫了個redis的session存貯類,僅供參考:github
<?php //namespace openyii\framework; class CSessionRedis //extends CSession { protected $redisInstance; //redis操做實例 public $keyPrefix; //鍵前綴 public $lifeTime; //生命週期 public function __construct( $keyPrefix,$lifeTime=3600 ) { $this->lifeTime = $lifeTime; $this->keyPrefix = $keyPrefix; session_set_save_handler( array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc') ); if ($this->keyPrefix === null) { $this->keyPrefix = substr(md5(base::$app->id), 0, 5); } session_start(); //以全局變量形式保存在一個session中而且會生成一個惟一的session_id, } function open($savePath, $sessionName) { //todo $redis redis的操做實例要本身寫下 $this->redisInstance = $redis; // $this->redisInstance = base::$app->redis; return true; } function close() { return true; } function read($id) { if( !$this->redisInstance->exists($this->calculateKey($id)) ){ $this->redisInstance->set( $this->calculateKey($id),'' ); } return $this->redisInstance->get( $this->calculateKey($id) ); } function write($id, $value) { return $this->redisInstance->set($this->calculateKey($id), $value, $this->lifeTime)?true:false; } function destroy($id) { $this->redisInstance->delete($this->calculateKey($id)); if( !$this->redisInstance->exists($this->calculateKey($id)) ){ return false; } return true; } function gc($lifetime) { //不作實現,redis自己有回收機制 return true; } /** * 加密key * @param $id * @return string */ protected function calculateKey($id) { return $this->keyPrefix . md5(json_encode([__CLASS__, $id])); } }
具體細節能夠看我寫的php框架代碼,若是想本身實現,上面的代碼改改就能夠實現的。web
mysql使用memory存貯引擎,設計數據表(sessionid、sessionValue 、過時時間),重寫session_set_save_handler方法。redis
沒想清楚,諮詢了老大,作法比較坑,php.ini修改配置指定redis master ip,有個守護進程的腳本檢測哨兵,發生故障哨兵切換,檢測到發郵件通知‘人’去手動修改php.ini master的ip,感受匪夷所思啊!算法
個人思路是:哨兵模式會有三個redis服務ip端口,客戶端鏈接獲取每一個redis服務的info信息,若是info裏包含master role,則將此master redis服務操做實例交給上面的代碼,實現寫操做。sql
後面本身動手實現了吧,可行的,代碼地址貼下,結合代碼及說明看看,但願能幫助你們理解:shell