【面试易错题】浏览器第一次请求,Tomcat就有sessionId啦?错!这个是一个误区!
sessionid 是一个会话的 key,浏览器第一次访问服务器会在服务器端生成一个 session,有一个 sessionid 和它对应。tomcat 生成的 sessionid 叫做 jsessionid。
session 在访问 tomcat服务器 HttpServletRequest 的 getSession(true) 的时候创建,tomcat 的 ManagerBase 类提供创建 sessionid 的方法:随机数 + 时间 + jvmid;
存储在服务器的内存中,tomcat 的 StandardManager 类将 session 存储在内存中,也可以持久化到 file,数据库,memcache,redis 等。客户端只保存 sessionid 到 cookie 中,而不会保存 session,session 销毁只能通过 invalidate 或超时,关掉浏览器并不会关闭 session。
那么 Session 在何时创建呢?
当然还是在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建 Session 的方法。
“而在 Java 中是通过调用 HttpServletRequest 的 getSession 方法(使用 true 作为参数)创建的。
”
在创建了 Session 的同时,服务器会为该 Session 生成唯一的 Session id,而这个 Session id 在随后的请求中会被用来重新获得已经创建的 Session;在 Session 被创建之后,就可以调用 Session 相关的方法往 Session 中增加内容了,而这些内容只会保存在服务器中,发到客户端的只有 Session id;当客户端再次发送请求的时候,会将这个 Session id 带上,服务器接受到请求之后就会依据 Session id 找到相应的 Session,从而再次使用之。
创建:
sessionid 第一次产生是在直到某 server 端程序调用 HttpServletRequest.getSession(true) 这样的语句时才被创建。
删除:
-
超时; -
调用 HttpSession.invalidate(); -
程序关闭;
session 存放在哪里:
服务器端的内存中。不过 session 可以通过特殊的方式做持久化管理(memcache,redis)。
session 的 id 是从哪里来?
sessionID 是如何使用的:当客户端第一次请求 session 对象时候,服务器会为客户端创建一个 session,并将通过特殊算法算出一个 session 的 ID,用来标识该 session 对象
session 会因为浏览器的关闭而删除吗?
不会,session 只会通过上面提到的方式去关闭。
tomcat 中 session 的创建
ManagerBase 是所有 session 管理工具类的基类,它是一个抽象类,所有具体实现 session 管理功能的类都要继承这个类,该类有一个受保护的方法,该方法就是创建 sessionId 值的方法:(tomcat 的 session 的 id 值生成的机制是一个随机数加时间加上 jvm 的 id 值,jvm 的 id 值会根据服务器的硬件信息计算得来,因此不同 jvm 的 id 值都是唯一的),
StandardManager 类是 tomcat 容器里默认的 session 管理实现类,
它会将 session 的信息存储到 web 容器所在服务器的内存里。
PersistentManagerBase 也是继承 ManagerBase 类,它是所有持久化存储 session 信息的基类,PersistentManager 继承了 PersistentManagerBase,但是这个类只是多了一个静态变量和一个 getName 方法,目前看来意义不大,对于持久化存储 session,tomcat 还提供了 StoreBase 的抽象类,它是所有持久化存储 session 的基类,另外 tomcat 还给出了文件存储 FileStore 和数据存储 JDBCStore 两个实现。
什么是 session 一致性问题?
只要用户不重启浏览器,每次 http 短连接请求,理论上服务端都能定位到 session,保持会话。
分布式 session
单服务器 web 应用中,session 信息只需存在该服务器中,这是我们前几年最常接触的方式,但是近几年随着分布式系统的流行,单系统已经不能满足日益增长的百万级用户的需求,集群方式部署服务器已在很多公司运用起来,当高并发量的请求到达服务端的时候通过负载均衡的方式分发到集群中的某个服务器,这样就有可能导致同一个用户的多次请求被分发到集群的不同服务器上,就会出现取不到 session 数据的情况,于是 session 的共享就成了一个问题。
如上图,假设用户包含登录信息的 session 都记录在第一台 web-server 上,反向代理如果将请求路由到另一台 web-server 上,可能就找不到相关信息,而导致用户需要重新登录。
Session 一致性解决方案
1.session 复制(同步)
思路:多个 web-server 之间相互同步 session,这样每个 web-server 之间都包含全部的 session
优点:web-server 支持的功能,应用程序不需要修改代码
不足:
-
session 的同步需要数据传输,占内网带宽,有时延
-
所有 web-server 都包含所有 session 数据,数据量受内存限制,无法水平扩展
-
有更多 web-server 时要歇菜
2. 客户端存储法
思路:服务端存储所有用户的 session,内存占用较大,可以将 session 存储到浏览器 cookie 中,每个端只要存储一个用户的数据了
优点:服务端不需要存储
缺点:
-
每次 http 请求都携带 session,占外网带宽
-
数据存储在端上,并在网络传输,存在泄漏、篡改、窃取等安全隐患
-
session 存储的数据大小受 cookie 限制
“端存储” 的方案虽然不常用,但确实是一种思路。
3. 反向代理 hash 一致性
思路:web-server 为了保证高可用,有多台冗余,反向代理层能不能做一些事情,让同一个用户的请求保证落在一台 web-server 上呢?
方案一:四层代理 hash
反向代理层使用用户 ip 来做 hash,以保证同一个 ip 的请求落在同一个 web-server 上
方案二:七层代理 hash
反向代理使用 http 协议中的某些业务属性来做 hash,例如 sid,city_id,user_id 等,能够更加灵活的实施 hash 策略,以保证同一个浏览器用户的请求落在同一个 web-server 上
优点:
-
只需要改 nginx 配置,不需要修改应用代码
-
负载均衡,只要 hash 属性是均匀的,多台 web-server 的负载是均衡的
-
可以支持 web-server 水平扩展(session 同步法是不行的,受内存限制)
不足:
-
如果 web-server 重启,一部分 session 会丢失,产生业务影响,例如部分用户重新登录
-
如果 web-server 水平扩展,rehash 后 session 重新分布,也会有一部分用户路由不到正确的 session
session 一般是有有效期的,所有不足中的两点,可以认为等同于部分 session 失效,一般问题不大。
对于四层 hash 还是七层 hash,个人推荐前者:让专业的软件做专业的事情,反向代理就负责转发,尽量不要引入应用层业务属性,除非不得不这么做(例如,有时候多机房多活需要按照业务属性路由到不同机房的 web-server)。
4. 后端统一集中存储
思路:将 session 存储在 web-server 后端的存储层,数据库或者缓存
优点:
-
没有安全隐患
-
可以水平扩展,数据库 / 缓存水平切分即可
-
web-server 重启或者扩容都不会有 session 丢失
不足:增加了一次网络调用,并且需要修改应用代码
对于 db 存储还是 cache,个人推荐后者:session 读取的频率会很高,数据库压力会比较大。如果有 session 高可用需求,cache 可以做高可用,但大部分情况下 session 可以丢失,一般也不需要考虑高可用。
总结
保证 session 一致性的架构设计常见方法:
-
session 同步法:多台 web-server 相互同步数据
-
客户端存储法:一个用户只存储自己的数据
-
反向代理 hash 一致性:四层 hash 和七层 hash 都可以做,保证一个用户的请求落在一台 web-server 上
-
后端统一存储:web-server 重启和扩容,session 也不会丢失
对于方案 3 和方案 4,个人建议推荐后者:
-
web 层、service 层无状态是大规模分布式系统设计原则之一,session 属于状态,不宜放在 web 层
-
让专业的软件做专业的事情,web-server 存 session?还是让 cache 去做这样的事情吧。
===myJavaKeKeArticleKeyId:2020111110001===
来源链接:
https://blog.csdn.net/wowwilliam0/article/details/82736074