Session的实现原理简介
背景:
1. 由于Http协议是无状态的,服务端如何识别客户端请求呢,只能依靠http报文中新增部分头字段来实现请求识别(如何在请求body或这参数中设置会员参数,服务器端会话就与自定义的会员识别绑定到一起)
2. 基于浏览器的web应用,请求都是有浏览器发起的,貌似也不能手动随便添加请求头(仅有XMLHttpRequest可以手动设置请求头),哪有没有一种可以由服务端生成,客户端请求是自动在请求中设置对应头字段的技术呢,这就是cookie
Cookie:
cookie是在客户端负责保存的,既可以客户端生成,也可以服务器端生成,Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie:
(1)内存Cookie由浏览器维护,保存在内存中,浏览器关闭后就消失了,其存在时间是短暂的
(2)硬盘Cookie保存在硬盘里,有一个过期时间,除非用户手工清理或到了过期时间,硬盘Cookie不会被删除
(3)cookie一些重要的属性,path,domain,maxAge,secure,httponly,可以自己去研究一下
(4)服务端生成cookie的响应头为 Set-Cookie:JSESSIONID=164A9B3B768FD959AA20505D4C09; Path=/; HttpOnly
(5)客户端发送http请求带的cookie请求头 Cookie:AMCV_niwodai%40AdobeOrg=-15069…7-badf-4795-9c64-eb9960c23d48
Session的实现原理:
(1)服务端首先查找对应的cookie的值(sessionid)
(2)根据sessionid,从服务器端session存储中获取对应id的session数据,进行返回
(3)如果找不到sessionid,服务器端就创建session,生成sessionid对应的cookie,写入到响应头中
session共享实现
传统的session由服务器端生成并存储,当应用进行分布式集群部署的时候,如何保证不同服务器上session信息能够共享呢?
两种实现方式:1.session集中存储(redis,memcached,hbase等),2. 不同服务器上session数据进行复制,两种方式的优缺点,大家应该一目了然
基于session集中存储的实现方案:
(1)新增Filter,拦截请求,包装HttpServletRequest
(2)改写getSession方法,从session存储中获取session数据,返回自定义的HttpSession实现
(3)在生成新Session后,写入sessionid到cookie中
Redis存储session的需要考虑问题:
1. session数据如何在Redis中存储?
2. session属性变更何时触发存储?
我们的实现:
考虑到session中数据类似map的结构,采用redis中hash存储session数据比较合适,如果使用单个value存储session数据,不加锁的情况下,就会存在session覆盖的问题,因此使用hash存储session,每次只保存本次变更session属性的数据,避免了锁处理,性能更好
如果每改一个session的属性就触发存储,在变更较多session属性时会触发多次redis写操作,对性能也会有影响,我们是在每次请求处理完后,做一次session的写入,并且之写入变更过的属性
如果本次没有做session的更改, 是不会做redis写入的,仅当没有变更的session超过一个时间阀值(不变更session刷新过期时间的阀值),就会触发session保存,以便session能够延长有效期