携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
3. 会话管理
Cookie和Session各有各的优点和缺点
1. Cookie
Cokie诞生原因
- HTTP 是无状态,有会话的
HTTP 是无状态的:在同一个连接中,两个执行成功的请求之间是没有关系的。这就带来了一个问题,用户没有办法在同一个网站中进行连续的交互,比如在一个电商网站里,用户把某个商品加入到购物车,切换一个页面后再次添加了商品,这两次添加商品的请求之间没有关联,浏览器无法知道用户最终选择了哪些商品。而使用 HTTP 的头部扩展,HTTP Cookies 就可以解决这个问题。把 Cookies 添加到头部中,创建一个会话让每次请求都能共享相同的上下文信息,达成相同的状态。
注意,HTTP 本质是无状态的,使用 Cookies 可以创建有状态的会话。
# HTTP Cookie
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会
在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏
览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。
Cookie应用场景
Cookie 主要用于以下三个方面:
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
Cookie 曾一度用于客户端数据的存储,因当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。由于服务器指定 Cookie 后,浏览器的每次请求都会携带 Cookie 数据,会带来额外的性能开销(尤其是在移动环境下)。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB 。
cookie优缺点
优点:可以解决HTTP 无状态的问题
缺点:Cookie存在在客户端,可能会造成隐私泄露;
另外浏览器每次发送给服务器Cookie会对流量产生影响
当浏览器发送cookie给服务器时,服务器接收cookie以后可能取里面的值用一下,或者在模板里用一下。
演示cookie
通过上面的文字,相信我们对 Cookie 已经有了简单的认识,下面写几个简单的小例子体会一下相关语法。
因为只是简单的小例子,不是什么业务,所以写到 AlphaController里,这里装着一些演示的小demo。
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// cookie 示例
@RequestMapping(path = "/cookie/set", method = RequestMethod.GET)
@ResponseBody
public String setCookie(HttpServletResponse response){
// 创建cookie,必须传入参数(没有无参构造器),且必须都是字符串
// 并且每一个cookie对象只能存一组字符串,即一个key-val
Cookie cookie = new Cookie("code", CommunityUtil.generateUUID());
// 设置cookie生效的范围,服务器首先发送给浏览器cookie,然后浏览器给服务器发请求需要发cookie
//需要指定浏览器给服务器的哪些路径请求发cookie,不必每个路径都发,因为可能不需要,会浪费网络资源
cookie.setPath("/community/alpha"); // 设置cookie只有在这个路径及子路径才有效
// cookie默认发到浏览器,浏览器关掉就消失
// 一旦给cookie设置了生存时间,它会存在硬盘里,长期有效,直到超过这个时间才会无效
// 设置cookie的生存时间
cookie.setMaxAge(60 * 10); // 单位是秒
// 将cookie放到response头里发送
response.addCookie(cookie);
return "set cookie";
}
@RequestMapping(path = "/cookie/get", method = RequestMethod.GET)
@ResponseBody
// @CookieValue("code")是获取cookie中key为"code"的value
// cookie需要在服务器和客户端之间传,所以只能存少量,另外客户端可以识别字符串,其他java类型无法识别,所以用key-value
public String getCookie(@CookieValue("code") String code){
System.out.println(code);
return "get cookie";
}
}
测试:
首先浏览器访问Controller的cookie/set方法,可以看到请求里面没有cookie,但是响应里面有cookie
然后浏览器访问Controller的cookie/get方法可以看到请求里面也有了cookie
2. Session
Sesson定义
是JavaEE的标准,用于在服务端记录客户端信息。
注:Session本质是依赖于cookie的,浏览器和服务器之间是多对一的关系,浏览器和Session之间的对应关系怎么找是依赖于cookie的(将sessionId存到cookie里发送)。
Session 优缺点
优点:
可以解决http无状态的问题
数据存放在服务器更加安全
缺点:
数据存放在服务器会增加服务器的压力
演示Session
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// session 示例
@RequestMapping(path = "/session/set", method = RequestMethod.GET)
@ResponseBody
public String setSession(HttpSession session){
// session 创建不需要我们手动创建,SpringMVC会自动创建session并帮我们注入进来,所以只需声明即可
// session 是一直存在在服务端的,所以它里面存什么数据都可以
session.setAttribute("id", 1);
session.setAttribute("name", "Test");
return "set session";
}
@RequestMapping(path = "/session/get", method = RequestMethod.GET)
@ResponseBody
public String getSession(HttpSession session){
System.out.println(session.getAttribute("id"));
System.out.println(session.getAttribute("name"));
return "get session";
}
}
3. 分布式使用Session存在什么问题,解决方案是什么
访问分布式中之前没访问过的服务器时,服务器里面没有存在之前的session,浏览器得不到之前的session
- 解决:黏性session,对于一个固定ip的请求,固定分给一个服务器处理。 缺点:分布式本来是解决负载均衡问题的,这种解决方法会导致负载不均衡问题。
- 解决:同步session,某一个服务器创建session并且存了数据之后,它会把这个session同步给其他的服务器 缺点:这种方法会对服务器性能产生一定的影响;服务器和服务器之间有了一定的关系,产生耦合,之间不是那 么的独立了。
- 解决:共享session,搞一台共享服务器,这个服务器不是处理业务的,而是专门用来存session的, 其他服务器都向这台服务器获取session, 缺点:如果共享session服务器宕机了,其他服务器都没办法工作了。
目前解决方案,能存到cookie就存到cookie,不能存到cookie的数据就存到关系型数据库里,数据库的数据存在硬盘里,存取比较慢,性能慢。但是存到 非关系型数据库NoSQL比如redis就快很多了
我们在前面开发的时候会话数据先存到数据库或者session(不适合存到mysql里的先存到session)里,在后面学习了redis之后的再迁移到redis里