memcached_banner

大型Web应用中使用memcache同步session

大型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;
	}
}

 

发表回复