默认情况下,PHP 使用内置的文件会话保存管理器(files)来完成会话的保存。我们无需设置,PHP默认将session以文件的形式保存到服务器。

通过调用函数 session_start() 即可手动开始一个会话。如果配置项 session.auto_start 设置为1, 那么请求开始的时候,会话会自动开始。

PHP也提供了自定义会话保存管理器功能。有时候我们希望session可以保存到其他地方,如数据库。

自定义Session存储函数

如果需要在数据库中或者以其他方式存储会话数据, 需要使用 session_set_save_handler() 函数来创建一系列用户级存储函数。

函数 session_set_save_handler() 的参数即为在会话生命周期内要调用的一组回调函数: openreadwrite 以及 close。 还有一些回调函数被用来完成垃圾清理:destroy 用来删除会话, gc用来进行周期性的垃圾收集。

PHP手册上说明了实现原理:

会话开始的时候,PHP 会调用 open 管理器,然后再调用 read 回调函数来读取内容,该回调函数返回已经经过编码的字符串。 然后 PHP 会将这个字符串解码,并且产生一个数组对象,然后保存至 $_SESSION 超级全局变量。

当 PHP 关闭的时候(或者调用了 session_write_close() 之后), PHP 会对 $_SESSION 中的数据进行编码, 然后和会话 ID 一起传送给 write 回调函数。 write 回调函数调用完毕之后,PHP 内部将调用 close 回调函数。

销毁会话时,PHP 会调用 destroy回调函数。

根据会话生命周期时间的设置,PHP 会不时地调用 gc 回调函数。 该函数会从持久化存储中删除超时的会话数据。 超时是指会话最后一次访问时间距离当前时间超过了 $lifetime 所指定的值。

需要实现:

session_set_save_handler ( callable $open , callable $close , callable $read , callable $write , callable $destroy , callable $gc [, callable $create_sid ] )

MySQL存储session示例:

<?php
/*
CREATE TABLE IF NOT EXISTS `sessions` (`id` varchar(40) NOT NULL,`ip_address` varchar(45) NOT NULL DEFAULT '',`timestamp` int(10) unsigned DEFAULT 0 NOT NULL,`data` blob NOT NULL,KEY `sessions_timestamp` (`timestamp`)
);
*/$con = mysqli_connect("127.0.0.1", "user" , "pass", "session");
function open($save_path, $session_name) {return true;
}function close() {return true;
}function read($id, $con) {if ($result = mysqli_query($con, sprintf("select * from sessions where `id`='%s' and `timestamp` > '%s'", $id, time()))) {if ($row = mysqli_fetch_row($result)) {return $row["data"];}}else {return "";}
}function write($id, $data, $con) {$expire = time() + SESSION_MAXLIFETIME;$sql = 'INSERT INTO `sessions` (`id`, `data`, `timestamp`) '. 'values (%s, %s, %s) '. 'ON DUPLICATE KEY UPDATE data = %s, timestamp = %s';if ($result = mysqli_query($con, $con, sprintf($sql, $id, $data, $expire, $data, $expire))) {return true;} else {return false;}
}function destroy($id, $con) {if ($result = mysqli_query($con, sprintf("delete * from sessions where id=%s", $id))) {return true;}else {return false;}
}function gc($maxlifetime, $con) {if ($result = mysqli_query($con, sprintf("delete * from sessions where timestamp < %s", time()))) {return true;} else {return false;}
}session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
session_start();

当然,也支持类里面的方法:

<?php
class MySQLSession{private $con;function open($save_path, $session_name) {$this->con = mysqli_connect("127.0.0.1", "user" , "pass", "session");}function close() {mysqli_close($this->con);}function read($id) {if ($result = mysqli_query($this->con, sprintf("select * from sessions where `id`=%s and `timestamp` > %s", $id, time()))) {if ($row = mysqli_fetch_row($result)) {return $row["data"];}}else {return "";}}function write($id, $data) {$expire = time() + SESSION_MAXLIFETIME;$sql = 'INSERT INTO `sessions` (`id`, `data`, `timestamp`) '. 'values (%s, %s, %s) '. 'ON DUPLICATE KEY UPDATE data = %s, timestamp = %s';if ($result = mysqli_query($this->con, sprintf($sql, $id, $data, $expire, $data, $expire))) {return true;} else {return false;}}function destroy($id) {if ($result = mysqli_query($this->con, sprintf("delete * from sessions where id=%s", $id))) {return true;}else {return false;}}function gc($maxlifetime) {if ($result = mysqli_query($this->con, sprintf("delete * from sessions where timestamp < %s", time()))) {return true;} else {return false;}}
}$handler = new MySQLSession();
session_set_save_handler(array($handler, 'open'),array($handler, 'close'),array($handler, 'read'),array($handler, 'write'),array($handler, 'destroy'),array($handler, 'gc')
);
// 下面这行代码可以防止使用对象作为会话保存管理器时可能引发的非预期行为
register_shutdown_function ( 'session_write_close' );session_start();

自 PHP 5.4 开始,可以使用下面的方式来注册自定义会话存储函数:

bool session_set_save_handler ( SessionHandlerInterface $sessionhandler [, bool $register_shutdown = true ] )

需要实现类里的open,write,read,destroy,gc,close方法。

<?phpclass  MySessionHandler  implements  SessionHandlerInterface{public function open($save_path, $session_id){return true;}/*** 写session*/public function write($session_id, $session_data){// TODO: Implement write() method.}/*** 读取session*/public function read($session_id){// TODO: Implement read() method.}/*** 删除指定session*/public function destroy($session_id){// TODO: Implement destroy() method.}/*** 销毁过期session*/public function gc($maxlifetime){// TODO: Implement gc() method.}public function close(){return true;}}$handler  = new  MySessionHandler ();session_set_save_handler ( $handler ,  true );session_start ();

分布式存储

当多台服务器负载均衡使用时,这时候还在使用默认的文件存储session方式,就会造成session不同步。这时候我们可以把session存储到同一个地方,如上面的mysql存储session方式。下面看看memcache、redis等如何存储session。

一些 PHP 扩展提供了内置的会话管理器,例如:redis, memcache, 可以通过配置项 session.save_handler 来使用它们。

对于文件会话保存管理器,会将会话数据保存到配置项 session.save_path 所指定的位置。
对于缓存类保存管理器,会将会话数据保存到配置项 session.save_path 所指定的地址。

Memcache

<?php
ini_set("session.save_handler", "memcache"); // memcache
ini_set("session.save_path", "127.0.0.1:11211"); // 不要tcp:session_start();$mem = new memcache();
$mem->addServer('127.0.0.1', '11211');//测试memcache是否正常
//$mem->add('uid', 6, 0, 3600);
//echo $mem->get('uid');//设置一个session
//$_SESSION['uid'] = 10;
//var_dump($_SESSION);//查看session在memcache里的存储
echo $mem->get(session_id());

Redis

<?php
ini_set("session.save_handler", "redis"); // memcache
ini_set("session.save_path", "127.0.0.1:6379"); // 不要tcp:session_start();$redis = new redis();
$redis->connect('127.0.0.1', '6379');//测试redis是否正常
//$redis->set('uid', 6, 3600);
//echo $redis->get('uid');//设置一个session
$_SESSION['uid'] = 10;
//var_dump($_SESSION);//查看session在redis里的存储
var_dump($redis->get('PHPREDIS_SESSION:'.session_id()));

实例

ThinkPHP3.13设置session入库

准备工作:

1、建立session表:

CREATE TABLE `thinkphp_session` (`session_id` varchar(255) NOT NULL,`session_expire` int(11) NOT NULL,`session_data` blob,UNIQUE KEY `session_id` (`session_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

这里的表名以实际为准。

2、修改配置文件
配置文件config.php中配置session的数据表,追加一下数据:

'SESSION_OPTIONS'       =>  array('type'=> 'db',//session采用数据库保存'name'                =>  'PHPSESSION',   //设置session名'expire'              =>  3600 * 24 * 1, //SESSION保存时间'use_trans_sid'       =>  1,                               //跨页传递'use_only_cookies'    =>  0,                               //是否只开启基于cookies的session的会话方式
),
'SESSION_TABLE'=>'thinkphp_session',

3、确保相关驱动存在,位于Core\Extend\Driver\Session:
SessionDb.class.php
该文件默认使用mysql系列函数连接,需要修改为mysqli:

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------defined('THINK_PATH') or exit();
/*** 数据库方式Session驱动*    CREATE TABLE think_session (*      session_id varchar(255) NOT NULL,*      session_expire int(11) NOT NULL,*      session_data blob,*      UNIQUE KEY `session_id` (`session_id`)*    );* @category   Extend* @package  Extend* @subpackage  Driver.Session* @author    liu21st <liu21st@gmail.com>*/
class SessionDb {/*** Session有效时间*/protected $lifeTime      = ''; /*** session保存的数据库名*/protected $sessionTable  = '';/*** 数据库句柄*/protected $hander  = array(); /*** 打开Session * @access public * @param string $savePath * @param mixed $sessName  */public function open($savePath, $sessName) { $this->lifeTime = C('SESSION_EXPIRE')?C('SESSION_EXPIRE'):ini_get('session.gc_maxlifetime');$this->sessionTable  =   C('SESSION_TABLE')?C('SESSION_TABLE'):C("DB_PREFIX")."session";//分布式数据库$host = explode(',',C('DB_HOST'));$port = explode(',',C('DB_PORT'));$name = explode(',',C('DB_NAME'));$user = explode(',',C('DB_USER'));$pwd  = explode(',',C('DB_PWD'));if(1 == C('DB_DEPLOY_TYPE')){//读写分离if(C('DB_RW_SEPARATE')){$w = floor(mt_rand(0,C('DB_MASTER_NUM')-1));if(is_numeric(C('DB_SLAVE_NO'))){//指定服务器读$r = C('DB_SLAVE_NO');}else{$r = floor(mt_rand(C('DB_MASTER_NUM'),count($host)-1));}//主数据库链接$hander = mysqli_connect($host[$w].(isset($port[$w])?':'.$port[$w]:':'.$port[0]),isset($user[$w])?$user[$w]:$user[0],isset($pwd[$w])?$pwd[$w]:$pwd[0]);$dbSel = mysqli_select_db($hander,isset($name[$w])?$name[$w]:$name[0]);if(!$hander || !$dbSel)return false;$this->hander[0] = $hander;//从数据库链接$hander = mysqli_connect($host[$r].(isset($port[$r])?':'.$port[$r]:':'.$port[0]),isset($user[$r])?$user[$r]:$user[0],isset($pwd[$r])?$pwd[$r]:$pwd[0]);$dbSel = mysqli_select_db($hander,isset($name[$r])?$name[$r]:$name[0]);if(!$hander || !$dbSel)return false;$this->hander[1] = $hander;return true;}}//从数据库链接$r = floor(mt_rand(0,count($host)-1));$hander = mysqli_connect($host[$r].(isset($port[$r])?':'.$port[$r]:':'.$port[0]),isset($user[$r])?$user[$r]:$user[0],isset($pwd[$r])?$pwd[$r]:$pwd[0]);$dbSel = mysqli_select_db($hander,isset($name[$r])?$name[$r]:$name[0]);if(!$hander || !$dbSel) return false; $this->hander = $hander; return true; } /*** 关闭Session * @access public */public function close() {if(is_array($this->hander)){$this->gc($this->lifeTime);return (mysqli_close($this->hander[0]) && mysqli_close($this->hander[1]));}$this->gc($this->lifeTime); return mysqli_close($this->hander);} /*** 读取Session * @access public * @param string $sessID */public function read($sessID) { $hander = is_array($this->hander)?$this->hander[1]:$this->hander;$res = mysqli_query($hander, "SELECT session_data AS data FROM ".$this->sessionTable." WHERE session_id = '$sessID'   AND session_expire >".time());if($res) {$row = mysqli_fetch_assoc($res);return $row['data']; }return ""; } /*** 写入Session * @access public * @param string $sessID * @param String $sessData  */public function write($sessID,$sessData) { $hander = is_array($this->hander)?$this->hander[0]:$this->hander;$expire = time() + $this->lifeTime; mysqli_query($hander, "REPLACE INTO  ".$this->sessionTable." (  session_id, session_expire, session_data)  VALUES( '$sessID', '$expire',  '$sessData')");if(mysqli_affected_rows($hander))return true; return false; } /*** 删除Session * @access public * @param string $sessID */public function destroy($sessID) { $hander = is_array($this->hander)?$this->hander[0]:$this->hander;mysqli_query($hander, "DELETE FROM ".$this->sessionTable." WHERE session_id = '$sessID'");if(mysqli_affected_rows($hander))return true; return false; } /*** Session 垃圾回收* @access public * @param string $sessMaxLifeTime */public function gc($sessMaxLifeTime) { $hander = is_array($this->hander)?$this->hander[0]:$this->hander;mysqli_query($hander, "DELETE FROM ".$this->sessionTable." WHERE session_expire < ".time());return mysqli_affected_rows($hander);} /*** 打开Session * @access public */public function execute() {session_set_save_handler(array(&$this,"open"), array(&$this,"close"), array(&$this,"read"), array(&$this,"write"), array(&$this,"destroy"), array(&$this,"gc"));}
}

经过测试,使用了函数session_set_save_handler后,session.save_handle后的值被修改为了user

转载于:https://www.cnblogs.com/52fhy/p/5495425.html

Session自定义存储及分布式存储相关推荐

  1. session的存储方式和配置

    Session又称为会话状态,是Web系统中最常用的状态,用于维护和当前浏览器实例相关的一些信息.我们控制用户去权限中经常用到Session来存储用户状态,这篇文章会讲下Session的存储方式.在w ...

  2. java 重写session_使用Shiro重写Session 自定义SESSION

    引入shiro的jar包 1.2.3 org.apache.shiro shiro-core ${shiroVersion} org.apache.shiro shiro-web ${shiroVer ...

  3. 修改session的存储机制

    <?php  //修改session的存储机制 //最起码应该有一个 读方法, 和一个 写方法. //1, 我们先去建立 读方法 和 写方法. //2, 告知session系统,使用我们的方法完 ...

  4. mysql标准化存储结构_Atitit.自定义存储引擎的接口设计 api 标准化 attilax 总结  mysql...

    Atitit.自定义存储引擎的接口设计api标准化attilax总结mysql 1.图16.1:MySQL体系结构 存储引擎负责管理数据存储,以及MySQL的索引管理.通过定义的API,MySQL服务 ...

  5. yii2 session mysql_Yii框架 session 数据库存储操作方法示例

    本文实例讲述了Yii框架 session 数据库存储操作方法.分享给大家供大家参考,具体如下: 在组件中声明session 组件开启数据库库存储 [ 'components' => [ 'db' ...

  6. 在Session中存储和获取

    Java后台在session中存储一个键值对 public ModelAndView login(HttpServletRequest request,HttpServletResponse resp ...

  7. bboss session自定义session id生成机制介绍

    bboss session自定义session id生成机制介绍 1.bboss session自定义session id生成接口 package org.frameworkset.security. ...

  8. 集中式存储和分布式存储

    集中式存储和分布式存储 集中式存储: RAID技术:(冗余磁盘阵列) 高效(多块磁盘并行读写) 安全(奇偶校验和热备技术) 常用的RAID类型 RAID0(条带化):多块硬盘逻辑成一块硬盘用(不具备冗 ...

  9. 集中式存储与分布式存储

    文章目录 集中式存储 概念 三种集中式存储结构 DAS NAS SAN 分布式存储 差别: 分布式存储优点 集中式存储 概念 SCSI接口 小型计算机系统接口:就是计算机主机内部设备之间(硬盘.软驱. ...

最新文章

  1. PLsql连接centos7上的Oracle的连接超时的解决方发
  2. Ubuntu下修改DNS重启也能用的方法
  3. STM32 电机教程 24 - ST MCLIB实战之无感变绝对式位置传感器
  4. 单月营收超 12 亿元,叮咚买菜如何携手神策数据造就增长神话?
  5. 16、17、18_使用gridspec定义多子图,条形图(Bar plots),分组条形图,堆叠条形图(Stacked bar chart),饼图(Pie plots),甜甜圈图,嵌套饼图
  6. mpvue template compiler 中文版教程
  7. com.alibaba.fastjson.JSONException: not close json text, token : :
  8. javaweb框架--自定义标签与freemaker结合
  9. 《天天数学》连载16:一月十六日
  10. 时间模块(import time)
  11. iPhone iPhoneSimulator.platform/Developer/usr/bin/clang++ failed with exit code 1 出现这种错误的原因是...
  12. stm32串口_【单片机】STM32串口基本配置
  13. 游戏即人生——《DOOM启世录》书评
  14. 修改VNR源码接入新版有道中文翻译API
  15. SAP HR系统2019年五一节假日调整
  16. android画布橡皮,Android画板开发之橡皮擦功能
  17. 免费实用的jpg转换成pdf工具
  18. LTspice基础教程-003.LTspice工具栏使用介绍
  19. ZOJ 3587 Marlon's String
  20. 联想记忆计算机网络,六种联想记忆方法详解

热门文章

  1. go语言打印日期_go语言基础:流程控制(4)-多重循环跳转控制
  2. java第一章_java 第一章
  3. 本地算术验证码识别教程
  4. Docker安装宝塔没想到竟然如此之简单~
  5. SpringBoot项目启动时控制台乱码,怎么办?
  6. linux 达梦C接口,达梦数据守护搭建
  7. 十二生肖配对表查询_天蝎座:分手后最容易复合的星座配对,一生分不开,最终重新走到一起...
  8. js如何监听元素事件是否被移除_js 监听事件的叠加和移除 -
  9. 约瑟夫问题java 递归_从约瑟夫问题的递归实现的问题说起
  10. MySQL中的调度器