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