大型WEB应用通常会采用集群进行负载均衡,但是php默认使用文件保存session,使用负载均衡后,会造成各台机器上session的不同步。如用户登录成功后,刷新页面,请求可能被分发到集群中的另外一台机器,而这台机器上是没有该用户的session信息的,则导致用户需要重新登陆。
解决该问题的方法通常有以下几种:
1.使用数据库同步session (性能会有一定影响)
2.session数据保存到cookie中(有cookie被伪造的风险)
3.使用memcache同步session(推荐)
下面是memcache配置:
vim /etc/php.d/memcache.ini
extension=memcache.so session.save_handler = "memcache" # cache server session.save_path = "tcp://host1:11211,tcp://host2:11211" session.gc_lifetime=14400 memcache.hash_strategy = consistent
使用方法
<?php session_start(); $_SESSION = new MemSession();
MemSession类代码
<?php /** * MemSession */ class MemSession implements ArrayAccess, Countable{ private $values = null; private $cache_key = null; private $memcache = null; private $logfile = null; private $session_name = null; public function __construct() { $this->session_name = session_name(); $this->cache_key = 'SESSION_'.$this->id(); header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0"); header('Pragma: non-cache'); header('Expires: Thu, 10 Nov 1981 08:52:00 GMT'); header('Vary: Accept-Encoding'); } private function getCacheObj() { if (!$this->memcache) { $this->memcache = MMemcache::Instance(); } return $this->memcache; } private function getValues() { $this->log('getValues:'.print_r($this->values, 1)); if (null === $this->values) { $memcache = $this->getCacheObj(); $this->values = $memcache->Get($this->cache_key); $this->log('getValuesFromCache:'.print_r($this->values, 1)); } } private function setValues() { $memcache = $this->getCacheObj(); $this->log('setValues:'.print_r($this->values, 1)); return $memcache->Set($this->cache_key, $this->values, time()+ini_get('session.gc_maxlifetime')); } public function offsetSet($key, $value) { $this->getValues(); if (is_null($key)) { $this->values[] = $value; } else { $this->values[$key] = $value; } $this->setValues(); } public function offsetExists($key) { $this->getValues(); return isset($this->values[$key]); } public function offsetUnset($key) { $this->getValues(); if (isset($this->values[$key])) unset($this->values[$key]); $this->setValues(); } public function offsetGet($key) { $this->getValues(); return isset($this->values[$key]) ? $this->values[$key] : null; } public function count() { $this->getValues(); return count($this->values); } public static function SetInfo($infoType, $data) { if ( empty($data) ) return; switch ($infoType) { case 'error': $_SESSION["__Info__$infoType"] = $data; break; case 'alert': $_SESSION["__Info__$infoType"] = $data;; break; case 'info': $_SESSION["__Info__$infoType"] = $data;; break; case 'reset_password': //fall down case 'invitation_id': $_SESSION["__Info__$infoType"] = $data;; break; case 'inviter_id': $_SESSION["__Info__$infoType"] = $data;; break; case 'pay_order_id': $_SESSION["__Info__$infoType"] = $data;; break; case 'pay_order_info': $_SESSION["__Info__$infoType"] = $data;; break; case 'pay_result': $_SESSION["__Info__$infoType"] = $data;; break; default: throw new Exception('info type not support'); } } public static function GetInfo($infoType='err', $useOnce=true) { if ( isset($_SESSION["__Info__$infoType"]) ) { $info_str = $_SESSION["__Info__$infoType"]; if ( $useOnce ) unset ($_SESSION["__Info__$infoType"]); return $info_str; } return null; } public function Refresh() { // } public function Destory() { setcookie($this->session_name, '', time()-42000, '/'); $memcache = $this->getCacheObj(); return $memcache->Del($this->cache_key); } public function getCacheKey() { return $this->cache_key; } private function log($data) { return true; } public function id() { $id = $_COOKIE[$this->session_name]; if (!$id) { $id = md5(uniqid(mt_rand())); setcookie($this->session_name, $id, time()+ini_get('session.cache_expire'), '/' ); } return $id; } }