搬运自博主xueqinna的CSDN博客,根据自己的理解修改了部分内容,介意者请前往原博主博客看原文。

今天就来彻底的学一下session是个啥,作者罗列了几个要点:
1.session 是啥?
2.怎么保存的?
3.如何运行?
4.有生命周期吗?
5.关闭浏览器会过期吗?
6.Redis代替文件存储session
7.分布式session的同步问题
 
SESSION是什么?

首先,我大致的知道,session是一次浏览器和服务器的交互的会话。

会话是什么呢?就是我问:你好吗?你回:恩,很好。就是一次会话,那么对话完成后,这次会话就结束了。

我们可以将一个变量存入全部的$_SESSION['name']中,这样PHP的各个页面和逻辑都能访问到,所以很轻松的用来判断是否登陆。

这是作者之前理解的SESSION,当然也是对的,只是解释的太肤浅,理解的太表面了,面试官如果听到这样的答案其实是不太满意的。

我参考了其他的很多资料,彻底理解清楚SESSION。

在说SESSION是什么之前,我们先来说说为什么会出现SESSION会话,它出现的机理是什么?我们知道,我们用浏览器打开一个网页,用到的是HTTP协议,学过计算机的应该都知道这个协议,它是无状态的,什么是无状态呢?就是说这一次请求和上一次请求是没有任何关系的,互不认识的,没有关联的。这种无状态的的好处就是一个字:快!

所以就会带来一个问题,比如:

我在www.a.com/login.php里面登录了,我在www.a.com/index.php 也希望是登录状态,但是,这是2个不同的页面,也就是2个不同的HTTP请求,这2个HTTP请求是无状态的,也就是无关联的,所以无法单纯的在index.php中读取到它在login.php中已经登录了!那怎么办呢?我们不能这2个页面我都去登录一遍吧。或者用笨方法这2个页面都去查询数据库,如果有登陆状态,就判断是已经登录了。这种查询数据库的方案虽然可行,但是每次都要去查询数据库,会造成数据库的压力。

正是这种诉求,这个时候,一个新的客户端存储数据方式出现了:COOKIE。

COOKIE是把少量的信息存储在用户自己的电脑上,它在一个域名下是一个全局的,只要设置它的存储路径在域名www.a.com下 ,那么当用户用浏览器访问时,php就可以从这个域名的任意页面读取cookie中的信息。所以就很好的解决了我在www.a.com/login.php页面登录了,我也可以在www.a.com/index.php获取到这个登录信息了。同时又不用反复去查询数据库。

虽然这种方案很不错,也很快速方便,但是由于cookie 是存在用户端,而且它本身存储的尺寸大小也有限,最关键是用户可以是可见的,并可以随意的修改,很不安全。

那如何又要安全,又可以方便的全局读取信息呢?于是,这个时候,一种新的存储会话机制:SESSION 诞生了。

从上面的描述来讲,它就是在一次会话中解决2次HTTP的请求的关联,让它们产生联系,让两个页面都能读取到找个这个全局的SESSION信息。SESSION信息存在于服务器端,所以也就很好的解决了安全问题。

SESSION的运行机制和是怎么保存的?

既然,它也是一种服务器存储数据的方式,肯定是存在服务器的某个地方。确实,它存在服务器的/tmp 目录下,这个目录与你的服务器配置(php.ini)有关,后面会有讲到。

我们先说下它的运行机制,是怎么分配的。我们主要讲 PHP 中 SESSION 的机制,其实各种语言都差不多。

如果这个时候,我们需要用到SESSION,那我们怎么办呢?第一步是开启SESSION:

session_start();

这是个无任何返回值的函数,既不会报错,也不会成功。它的作用是开启session,并随机生成一个唯一的32位的session_id,类似于这样:

m6v4h247qv0bri5car3h9pt8f1

SESSION的全部机制也是基于这个session_id,它用来区分哪几次请求是一个人发出的。为什么要这样呢?因为HTTP是无状态无关联的,一个页面可能会被成百上千人访问,而且每个人的用户名是不一样的,那么服务器如何区分这次是小王访问的,那次是小李访问的呢?所以就有了找个唯一的session_id 来绑定一个用户。一个用户在一次会话上就是一个session_id,这样成千上万的人访问,服务器也能区分到底是谁在访问了。

我们做个试验,看看,是不是这样的:

我们在php.a.com 域名下的a.php 页面中,输入如下代码:

session_start();
$a = session_id();
if(empty($a)) {session_start();}
echo "SID: ".SID."<br>session_id(): ".session_id()."<br>COOKIE: ".$_COOKIE["PHPSESSID"];

我们访问一下a.php页面,看能输出什么?

我们看到居然还有一个警告。我们先一个一个的看。首先SID这个常量,我们没有给它赋值,它居然能有输出,其次session_id()这个系统方法是输出本次生成的session_id。最后$_COOKIE['PHPSESSIID'] 没有值,这个我们接下来说。

好,我们再刷新这个页面,我们能看到什么?

奇怪的事情发生了。SID 没有值了,$_COOKIE['PHPSESSID']中有值了。而且,2次刷新,session_id 都是一样

的:rophcl4rt483cqgptjo9uedg91,实际上,只要不关闭网页,怎么刷新都会是一样的:

既然我们看到COOKIE中有值了,我们,打开firebug开看到底是什么:

而且这个PHPSESSID的过期时间是会话,什么意思呢?就是浏览器只要不关就一直不存,浏览器一关就过期,消失了。

好,我们关掉浏览器,重新打开a.php页面,看看有没有什么变化:

你看,是不是又回到当初第一次打开时候的样子。

OK,解惑的时候到了:

每次我们访问一个页面,如果有开启SESSION,也就是有session_start() 时,就会自动生成一个session_id 来标注是这次会话的唯一ID,同时也会自动往COOKIE里写入一个名字为PHPSESSID的变量,它的值正是session_id,当这次会话没结束,再次访问的时候,服务器会去读取这个PHPSESSID的COOKIE是否有值有没过期,如果能够读取到,则继续用这个session_id,如果没有,就会新生成一个session_id,同时生成PHPSESSID这个COOKIE。由于默认生成的这个PHPSESSID COOKIE是会话,也就是说关闭浏览器就会过期,所以,下次重新浏览时,会重新生成一个session_id。

好,这个是session_id,就用来标识绑定一个用户的,既然session_id生成了。那么当我们往session里面写入数据,是如何保存的,答案是保存在服务器的临时目录里,根据php.ini的配置,我机子上的这个session是存在E:\phpStudy\tmp\tmp目录里的。我们先说是存在这个目录下,然后待会儿讲如何修改。

那么它是怎么存的呢?

同样也是用到session_id。session_id是32位的,服务器会用 sess_sessionid 的形式存在这个临时目录下,比如上面这个例子:

所以,每一次生成的session_id都会生成一个这样的文件,用来保存这次会话的session信息。

我们往session里写入些数据,来看看session是怎么往这个文件里写数据的,我们同样在a.php页面继续加上写入session的语句:

$_SESSION['hello'] = 123;
$_SESSION['word'] = 456;

然后,我刷新页面,由于我并没有关闭页面,就这是说这次会话还没结束,那么肯定还会是同样的session_id : u67pn1hs2mdael3phpagk2h6l5

然后,我们 用编辑器打开它的存储文件sess_u67pn1hs2mdael3phpagk2h6l5这个文件,看看里面是什么?

是序列化的数据,我们肉眼也能读出来。当我们往$_SESSION全局变量里写数据时,它会自动往这个文件里写入。读取SESSION的时候,也会根据session_id 找到这个文件,然后读取需要的SESSION变量。

这个sess文件不会随着客户端的PHPSESSID过期一起过期,它会一直存在,除非GC扫描到它过期或者使用session_destroy()函数摧毁,我们在下面讲到出息GC回收的时候会说到。

我们大致总结下:

HTTP请求一个页面后,如果用到开启SESSION,会去读COOKIE中的PHPSESSID是否有,如果没有,则会新生成一个session_id,先存入COOKIE中的PHPSESSID中,再生成一个sess_sessionid文件。当有写入$_SESSION的时候,就会往sess_sessionid文件里序列化写入数据。当读取的SESSION变量的时候,先会读取COOKIE中的PHPSESSID,获得session_id,然后再去找这个sess_sessionid文件,来获取对应的数据。由于默认的PHPSESSID是临时的会话,在浏览器关闭后,会消失,所以,我们重新访问的时候,会新生成session_id和sess_sessionid这个文件。

好。SESSION生成和保存讲清楚了。我们再来看前面提到的几个变量:

echo "SID: ".SID."<br>";//SID 是一个系统常量,SID包含着会话名以及会话 ID 的常量,格式为 "name=ID",或者如果会话 ID 已经在适cookie 中设定时则为空字符串,第一次显示的时候输出的是SID的值,当你刷新的时候,因为已经在cookie中存在,所以显示的是一个空字符串。
echo "session_id(): ".session_id()."<br>";//session_id() 函数用来返回当前会话的session_id,它会去读取cookie中的name,也就是PHPSESSID值。
echo "COOKIE: ".$_COOKIE["PHPSESSID"];//$_COOKIE 变量用于取回 cookie 的值。

SESSION的相关配置

前面零星的提到了php.ini里面有关于SESSION相关的配置。我们打开php.ini来,搜索SESSION相关,我主要把用到的几个给列出来:

[Session]
session.save_handler = files //表示的是session的存储方式,默认files,save_handler 不仅仅只能用文件files,还可以用我们常见的memcache 和 redis 来保存。这个我们后面来说。
session.save_path = "d:/wamp/tmp" //session的存储路径
session.use_cookies = 1 //默认是1,表示会在cookie里创建名为PHPSESSID的session_id
session.name = PHPSESSID //配置cookie中session_id的名称,默认为PHPSESSID
session.auto_start = 0 //表示是否需要自动开启session,默认不开启,可用session_start()函数开启
session.cookie_lifetime = 0 //设置在客户端生成PHPSESSID这个cookie的过期时间,默认0。如果想关闭浏览器会话后,希望session信息能够保持的时间长一点,可以把这个值设置大一点,单位是秒。
session.serialize_handler = php //session序列化机制,php、wddx(xml),安装扩展后还有igbinary(二进制)
session.gc_divisor = 1000 //GC启动概率的基数
session.gc_probability = 1 //GC启动概率的指数
session.gc_maxlifetime = 1440 //sess_xxx文件的过期时间
//session.gc_divisor 与 session.gc_probability 合起来定义了在每个会话初始化时启动 gc(garbage collection 垃圾回收)进程的概率。此概率用 gc_probability/gc_divisor 计算得来。
//例如 1/100 意味着在每个请求中有 1% 的概率启动 gc 进程。session.gc_divisor 默认为 100。

主要我们用到的,常见的大概就是这几个。

举个栗子:session.gc_maxlifetime=30,session.gc_divisor=1000,session.gc_probability=1,就表示每一千个用户调用session_start()的时候,就百分百的会执行一次垃圾回收机制,将磁盘上没用的session文件删除。

session的垃圾回收,可参考session的垃圾回收机制

我们通过上面的各种,已经清楚session的种种了,它会产生各种的sess_前缀的文件,久而久之就会形成垃圾数据,而且正常的session读取也会造成压力,所以及时的清理是蛮有用的。

1. 代码处理

php代码中有几个函数是用来清理过期的session信息的,主要是这几个:

unset($_SESSION['hello']); //销毁标量,将sess_xxx的文件的hello变量删除,其他的变量都保持不变。
session_unset();//销毁sess_xxx文件中的所有变量,但sess_xxx文件还是保存着。
session_destroy();//将sess_xxx文件删除
setcookie(session_name(), '', time()-42000, '/');
//一般退出操作里面,我们也会将session_name() 获得到的PHPSESSID也给过期掉,删掉,因为网页没关,不这样删除的话,刷新之后,这个值是存在的,服务器将会重新创建一个一模一样session_id的sess文件。

2. php gc 自动删除

php.ini中的几个销毁sess_xxx文件的配置,在上面说了:

session.gc_divisor = 1000
session.gc_probability = 1
session.gc_maxlifetime = 1440

简单说下,php触发gc删除过期的sess_x的文件的概念是这样计算的:概率= gc_probability/gc_divisor,上面的默认的参数,也就是说概念是1/1000的概率,在页面启动session_start() 函数时候,会触发gc删除过期的sess_文件。这个概率其实是蛮小的

所以,我们可以将这个概念调整大一点,比如:将gc_probability 也调成1000,那gc_probability/gc_divisor 就等于1了,也就是百分一百会触发。这样就垃圾回收概率就大的多。

用redis存储session

上面说了很多关于session的存储啊机制等。现在说说如果用redis 存储session。之前说的都是用文件files存储,现在想用redis,好处有哪些?

更快的读取和写入速度。redis是直接操纵内存数据的,肯定是要比文件的形式快很多。

更好的设置好过期时间。文件存储的sess_sdewfrsf文件其实被删除掉还是要考运气的和概率的,很有可能造成sess_文件没即时删除,造成存储磁盘空间过多,和读取SESSION就变慢了。

更好的分布式同步。设置redis 的主从同步,可以快速的同步session到各台web服务器,比文件存储更加快速。

总的说来,用redis来存储SESSION速度更快,性能更高。

要做的第一件事,当然就是安装redis了。具体安装和配置php与redis,就不细说了,可以参考作者写的redis相关:redis安装与配置

redis 安装好了之后,接下来就是修改php.ini了。将原来的files 改成redis:

session.save_handler = redis

session.save_path = "tcp://127.0.0.1:6381"

需要用到tcp来连接redis,如果你设置reids 有密码访问的话,这样加上就可以了:tcp://127.0.0.1:6381?auth=authpwd

重启web服务器后,你就可以正常使用SESSION了。和之前使用files存储SESSION一模一样。

我们看下redis 是怎么存储session的。它是用了有别于文件存储使用sess_前缀的名字,它用PHPREDIS_SESSION: 前缀,再加上session_id 的形式,是一个string 字符串类型,带生存周期的。

PHPREDIS_SESSION:i9envsatpki9q8kic7m4k68os5

你会发现,它的值和文件存储session一模一样,都是用php序列化后存储,而且有明确的过期时间,是根据配置:session.gc_maxlifetime = 1440 来设定的,默认1440秒。当然你可以修改成其他的。

我们写入和读取每页还是一模一样,包括删除和情况,都是一模一样,没有什么变化:

session_start(); //开启session,如果读不到cookie,会重新生成一个session_id,redis里面也会新生成一个。
echo "SID: ".SID."<br>";
echo "session_id(): ".session_id()."<br>";
echo "COOKIE: ".$_COOKIE["PHPSESSID"];
$_SESSION['hello'] = 123; // 写入session 。会序列化后写入redis中
$_SESSION['word'] = 456;
var_dump($_SESSION['word']); //读session。会从redis读到,解序列后,读出这个值。redis 1440秒过期后,将读不到。
unset($_SESSION['hello']); // 删除 hello 的session 。会删除 redis的hello值
session_unset(); // 清空redis 中这个session_id的所有值。
session_destroy(); // 删除掉这个PHPREDIS_SESSION:i9envsatpki9q8kic7m4k68os5 key。

session同步

在做了web集群后,你肯定会首先考虑session同步问题,因为通过负载均衡后,同一个IP访问同一个页面会被分配到不同的服务器上,如果不同的服务器用的是不同的reidis服务,那么可能就会出现,一个登录用户,一会是登录状态,一会又不是登录状态。所以session这个时候就要同步了。刚好,我们选择用redis作为了存储,是可以在多台redis 服务器中同步的。

具体可以搜索 reidis主从同步或者redis 集群

参考资料:
http://zhidao.baidu.com/link?url=2_phukSt0xI6SSIVKUE37TxzivLqdCz_JCPhIUPLMB3TX_IWgoVKL2lwDn1Gh7xTykyV3ezU1YQv9s6HD3uhO_

http://blog.sina.com.cn/s/blog_5f54f0be0100xs7e.html
http://star0708.blog.163.com/blog/static/181091248201341710100381/
http://baike.baidu.com/view/25258.htm?fr=aladdin
http://www.cnblogs.com/hongfei/archive/2012/06/17/2552434.html

彻底弄清楚session是什么?的更多相关文章

  1. 彻底弄懂session,cookie,token

    session,cookie和token究竟是什么 简述 我在写之前看了很多篇session,cookie的文章,有的人说先有了cookie,后有了session.也有人说先有session,后有co ...

  2. 接口测试彻底弄懂Session、Cookie、Token的区别及联系hold住面试官--hold住了开3万,hold不住开3K!

    一.前言:接口测试之伤:cookie,session,token本是一家! cookie,session,token的区别早就已经成为测试同行的心病,各大论坛,各大博客,各大视频网站无不充斥着各种疑问 ...

  3. 彻底弄清楚session,cookie,sessionStorage,localStorage的区别及应用场景(面试向)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_94 客户端状态保持是一个老生常谈的问题了,归根结底追踪浏览器的用户身份及其相关数据无非就是以下四种方式:session,cooki ...

  4. 分布式中使用Redis实现Session共享(二)

    上一篇介绍了一些redis的安装及使用步骤,本篇开始将介绍redis的实际应用场景,先从最常见的session开始,刚好也重新学习一遍session的实现原理.在阅读之前假设你已经会使用nginx+i ...

  5. Apache + Tomcat + mod_jk实现集群服务及session共享

    实现效果:用apache 分发请求到tomcat中的对应的项目 原理:

  6. HttpURLConnection、HttpClient和Session

    原文地址:http://www.cnblogs.com/kross/p/3615695.html 一直没弄懂Session,cookies什么的登陆验证到底是怎么回事,昨天分别用HttpURLConn ...

  7. [转]分布式中使用Redis实现Session共享(二)

    本文转自:http://www.cnblogs.com/yanweidie/p/4678095.html 上一篇介绍了一些redis的安装及使用步骤,本篇开始将介绍redis的实际应用场景,先从最常见 ...

  8. (转)分布式中使用Redis实现Session共享(二)

    上一篇介绍了一些redis的安装及使用步骤,本篇开始将介绍redis的实际应用场景,先从最常见的session开始,刚好也重新学习一遍session的实现原理.在阅读之前假设你已经会使用nginx+i ...

  9. 29、前端知识点--session\cookie\token

    Java Token的原理和生成使用机制 https://yq.aliyun.com/articles/594217 Cookies Session Token 三者区别及应用场景 https://w ...

随机推荐

  1. C语言函数--H

    函数名: harderr 功 能: 建立一个硬件错误处理程序 用 法: void harderr(int (*fptr)()); 程序例: /*This program will trap disk ...

  2. 【Android开发VR实战】三.开发一个寻宝类VR游戏TreasureHunt

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53939303 本文出自[DylanAndroid的博客] [Android开发 ...

  3. bzoj3033 太鼓达人——欧拉图搜索

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3033 考虑那 (1<<k) 个数,要形成答案,必然是相邻两个数间有 k-1 个重 ...

  4. Android网络连接监听

    接收系统网络服务的广播接收者 public class NetStateReceiver extends BroadcastReceiver { private Handler handler; pu ...

  5. json用法

    什么是JSON? JavaScript 对象表示法(JavaScript Object Notation). JSON是一种轻量级的数据交换格式,某个JSON格式的文件内部譬如可以长成这样: 1 2 ...

  6. HTML多媒体标记之字幕标记

    在HTML中,可以向页面中插入字幕,水平或垂直滚动显示文字信息,字幕标记的格式如下: <marquee 属性="值"...>滚动的文字信息</marquee> ...

  7. [Swift]LeetCode1064. 不动点 | Fixed Point

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  8. [转]利用 NPOI 變更字體尺寸及樣式

    本文转自:http://blog.cscworm.net/?p=1650 利用 NPOI 變更字體尺寸及樣式: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ...

  9. Windows phone开发之文件夹与文件操作系列(一)文件夹与文件操作

    Windows phone7中文件的存储模式是独立的,即独立存储空间(IsolatedStorage).对文件夹与文件操作,需要借助IsolatedStorageFile类. IsolatedStor ...

  10. 自动换行 word-break:break-all和word-wrap:break-word

    1.word-break:break-all;当内容(比如很长的一个单词)到每行的末端时,它会把单词截断显示一部分,下一行显示后一部分. 2.word-wrap:break-word;当内容(比如很长 ...