session和会话保持

1、session的基本介绍

session 是存储在服务器端的,所以称之为服务器端会话技术。session 能在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中,即 HttpSession 对象。

在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的 session 中,当用户使用浏览器访问其它程序时,其它程序可以从用户的 session 中取出该用户的数据,为用户服务。

Session 代表着服务器和客户端一次会话的过程。Session 对象存储特定用户会话所需的信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的信息将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。也就是说,session 的作用是能够让服务器在某一用户登录之后,用户进行的所有操作都保持在同一会话之内,这样就能够记住用户的状态,并且能记住用户在这次会话中进行的一系列操作和保存的信息。

session 存储在服务器端,session 是用来存储信息的,在 cookie 里面插入的只是 session id,是识别某一 session 的唯一标识。

 

1.1、会话

在程序中,会话跟踪是很重要的事情。会话保持是负载均衡最常见的问题之一,会话保持有时候又叫做粘滞会话(Sticky Sessions)。会话保持是指在负载均衡器上的一种机制,可以识别客户端与服务器之间交互过程的关联性,在作负载均衡的同时还保证一系列相关联的访问请求会分配到一台服务器上。

浏览器一旦给服务器发送请求,会话即建立。关掉该浏览器则认为会话结束,新开一个浏览器请求则认为又是一个新的会话。

Web应用程序是使用HTTP协议传输数据的,HTTP协议是无状态的协议,一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接,这就意味着服务器无法从连接上跟踪会话,通俗点来说下一个接口无法知道上一个接口做了什么,保存了什么信息等。比如用户 A 将一件商品放入购物车内,当再次将商品加入服务器(又调用了一次接口),此时已经无法判断该购买行为是属于用户 A 的会话还是用户 B 的会话了。要跟踪该会话,必须引入一种机制。Cookie 就是这样的一种机制,它可以弥补HTTP协议无状态的不足。在 Session 出现之前,基本上所有的网站都采用 Cookie 来跟踪会话。

 

1.2、什么时候需要会话保持

比如在某些要求登录状态的情境下,要求客户端和服务器之间保持一个会话(session)以记录客户端的各种信息。比如在大多数电子商务的应用系统或者需要进行用户身份认证的在线系统中,一个客户与服务器经常经过好几次的交互过程才能完成一笔交易或者是一个请求的完成。由于这几次交互过程是密切相关的,服务器在进行这些交互过程的某一个交互步骤时往往需要了解上一次或上几次的交互过程处理结果,这就要求所有这些相关的交互过程都由一台服务器完成,而不能被负载均衡器分散到不同的服务器上。否则可能出现异常情景:

1)客户端已经输入了正确的用户名和口令,但进行一些操作服务器并不知道该客户已登录,所以返回未登录提示信息,客户端反复跳到登录页面;
2)用户输入了正确的验证码,但是总提示验证码错误
3)客户端放入购物车的物品丢失,因为没有记住上次交互保存的信息


因此会话保持机制的意义就在于,确保在合适的情境下,将来自相同客户端的请求转发至后端相同的服务器进行处理。换句话说,就是将客户端与服务器之间建立的多个连接,都发送到相同的服务器进行处理。如果在客户端和服务器之间部署了负载均衡设备,很有可能这多个连接会被转发至不同的服务器进行处理。如果服务器之间没有会话信息的同步机制,会导致其他服务器无法识别用户身份,造成用户在和应用系统发生交互时出现异常。

 

1.3、基于session的会话保持实现

用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session,请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器,浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名。

当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。

每个用户第一次访问服务器后,会自动获得一个Session ID。如果用户在一段时间内没有访问服务器,那么Session会自动失效,下次即使带着上次分配的Session ID访问,服务器也认为这是一个新用户,会分配新的Session ID。

 

1.4、基于session实现保持登录状态

在用户登录完成过后,我们可以给 session 里面放置一些用户信息,这样当会话一直保持即浏览器未关闭时,保存在浏览器端的 sessionid 将一直不变,浏览器每次请求时会把该 sessionid 发送过来,如果通过该 sessionid 拿到的 session 中有用户信息,则表示该用户已登录,则允许执行其他操作。否则如果没拿到用户信息,则认为未登录,给浏览器返回未登录信息内容,由前端控制跳转到登录页。

 

2、HttpSession对象

HttpSession 对象是一个域对象,可以在一次会话的多次请求间共享数据。  

 

2.1、HttpSession对象的基本使用

  • 通过servl的requset对象来获取HttpSession对象:HttpSession session = request.getSession();
  • 存储数据到httpsession对象:public void setAttribute(String name, Object value);
  • 从httpsession对象获取数据:public Object getAttribute(String name);
  • 移除数据:public void removeAttribute(String name)

代码示例:

@WebServlet("/sessiontest01")
public class Session01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();   //获取httpsession对象
        session.setAttribute("msg", "hahaha");    //往httpsession对象存储数据
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}
@WebServlet("/sessiontest02")
public class Session02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        Object msg = session.getAttribute("msg");   //获取数据
        System.out.println(msg);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

先访问 sessiontest01 接口,该接口将会往session对象中存储数据。然后访问 sessiontest02 接口,我们就可以看到从  sessiontest02 接口当中可以拿到 sessiontest01 存储的数据。

 

2.1.1、session的原理

从上面我们可以知道,在一次会话当中,两个请求获取到的 httpsession 对象实际上是同一个对象。那么服务器是怎么确保在一次会话范围内,多个请求获取到的session对象是同一个呢?

事实上,服务器在第一次获取 session 即调用 request.getSession() 的时候,服务器会创建一个 session 对象(session 是一个集合,并且是一个map集合),并且存入服务器的 session 集合中以 sessionId 为标识键,也就是说根据 sessionId 即可取到对应 session 的引用。同时也会创建一个键名为 JSESSIONID 的 cookie 并且返回给浏览器,该 cookie 的值即为 sessionId。

如果客户访问同一域的其他 Servlet,这个存储着 sessionId 的 cookie 就会跟着请求上传到服务器。此时如果请求的另一个Servlet也要使用 session,服务器会自动检查有没有这个保存 sessionId 的 cookie,如果有则直接到 session 集合中取对应的 session 引用返回给要使用的Servlet。所以说,在同一会话当中,不管请求哪个Servlet,拿到的都是同一个session。

我们这里所说的session都是基于cookie机制来实现的,所以可以说,session的实现是依赖于cookie的。

 

比如下面,在我们第一次访问 sessiontest02 的servlet (该servlet内调用了request.getSession()获取session对象)时,服务器会自动返回一个创建名为 JSESSIONID 的cookie的响应头。当我们未关闭浏览器即未结束当前会话时,再次访问该请求,你会发现,此时该请求将不会再发送这个响应头回来。

sessiontest02 的代码:

@WebServlet("/sessiontest02")
public class Session02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //调用req.getSession()方法获取session对象
        HttpSession session = req.getSession();
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

可以看到,sessiontest02 调用了 request.getSession() 来获取 session 对象。如果该 servlet 没有调用该方法获取 session 对象的话,访问该 servlet 是不会自动创建名为 JSESSIONID  的 cookie 的。

 

2.1.2、JSESSIONID的cookie的创建时间

在我们创建会话时,即调用 request.getSession() 的时候,如果浏览器此时并不存在名为 JSESSIONID 的cookie,服务器就会自动创建一个名为 JSESSIONID 的 cookie 并返回给浏览器。

注意,在访问 html 时是不会创建session的,而 JSP 页面默认是会创建session的,我们也可以在JSP页面里面关掉自动创建session。

 

2.2.2、request.getSession()方法干了什么

request.getSession() 会去获取请求头的名为 JSESSIONID 的 cookie。如果浏览器此时并不存在名为 JSESSIONID 的cookie,服务器就会自动创建一个名为 JSESSIONID 的 cookie 并返回给浏览器。如果该 cookie 已存在,则直接到 session 集合中取对应的 session 引用返回给要使用的Servlet。所以 session 有没有失效取决于存不存在一个名为 JSESSIONID 的 cookie ,所以其实我们可以手动去创建一个名为 JSESSIONID 的 cookie ,然后手动去设置该 cookie 的过期时间并返回给浏览器,这样的话即使关闭了浏览器该 cookie 也不会失效,session 同样不会失效,此时多个 servlet 获取到的 session 对象仍是同一个。

 

3、session的过期时间

session 的默认失效时间为 30 分钟。session 的过期时间是从 session 不活动的时候开始计算,如果 session 一直活动,session就总不会过期。从该 Session 未被访问,开始计时,一旦Session被访问,服务器端可以在每次响应时刷新 session 的过期时间,即计时会重新清 0。只要调用 servlet 传送了该存储着 sessionid 的 cookie 则表示该 session 一直在活动,不一定要调用那种获取或者操作了 httpsession 对象的 servlet。

session 的默认失效时间是由服务器进行配置,tomcat 服务器可以在安装目录下的 conf/web.xml 文件下看到,如下所示:

上面的时间单位是分钟,如果是 -1 的话则表示永不失效。

当然我们也可以在项目中 web.xml 中配置相同的选项来覆盖掉 tomcat 的配置。或者可以直接在 Java 代码中设置 session 的过期时间:

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        session.setMaxInactiveInterval(30*60);    //以秒为单位
}

三种方式优先级:web容器 < web项目 < java代码。

 

另外,默认情况下,关闭浏览器或者重启服务器,session 都会失效。当我们关闭浏览器再重启,访问服务器时服务器会重新创建一个 session,所以默认情况下,关闭浏览器 session 则失效,关闭浏览器则意味着会话结束。因为默认情况下,sessionid 是存放在一个名为JSESSIONID 的cookie下的,而 cookie 默认情况下在浏览器关闭时即失效。

 

3.1、session的钝化和活化

  • 钝化:当服务器正常关闭时,还存活着的session(在设置时间内没有销毁) 会随着服务器的关闭被以文件(“SESSIONS.ser”)的形式存储在tomcat 的work 目录下,这个过程叫做Session 的钝化。
  • 活化:当服务器再次正常开启时,服务器会找到之前的“SESSIONS.ser” 文件,从中恢复之前保存起来的Session 对象,也就是将session文件转化为内存中的session对象,这个过程叫做Session的活化。

钝化和活化的过程服务器会自动帮我们实现。通过钝化和活化,在我们正常关闭服务器后(注意,并未关闭浏览器),再重启服务器,然后前端再次请求服务器时,此时仍然能够拿到关闭之前的 session 存储的数据。服务器重启前后给浏览器返回的 sessionid 并不一样,但仍能拿到关闭之前的 session 存储的数据。

(注意,在IDEA集成开发工具里可能看不到这个效果,我们可以将项目放在 tomcat 的安装目录下就可以看到钝化和活化的效果。)

 

4、session的特点

  • session 可用于存储一次会话间多次请求的数据,session的数据保存在服务器端,存储在服务器的内存中,tomcat 的 StandardManager 类将 session 存储在内存中,也可以持久化到 file,数据库,memcache,redis等。
  • session 可以存储任意类型,任意大小的数据。使用Session时,由于服务器把所有用户的Session都存储在内存中,如果遇到内存不足的情况,就需要把部分不活动的Session序列化到磁盘上,但这会大大降低服务器的运行效率。因此,放入Session的数据尽量不要太大

 

4.1、session 和 cookie 的区别

  • 作用范围不同,Cookie 保存在客户端(浏览器),Session 保存在服务器端。
  • 存取方式的不同,Cookie 只能保存 ASCII,Session 可以存任意数据类型
  • 隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
  • 存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。

 

5、一个服务器如何根据 session 来区分不同的用户

服务器是根据 session id 来区分用户的。当服务器接收到用 cookie 传过来的 session id 时,服务端会通过对比自身存储的 session Id 来判断用户之前是否存在,并返回对应的内容给不同用户。

 

6、服务器集群如何应用session

在使用多台服务器构成集群时,使用Session会遇到一些额外的问题。通常,多台服务器集群使用反向代理作为网站入口:

 

如果多台Web Server采用无状态集群,那么反向代理总是以轮询方式将请求依次转发给每台Web Server,这会造成一个用户在Web Server 1存储的Session信息,在Web Server 2和3上并不存在,即从Web Server 1登录后,如果后续请求被转发到Web Server 2或3,那么用户看到的仍然是未登录状态。

要解决这个问题,方案一是在所有Web Server之间进行Session复制,但这样会严重消耗网络带宽,并且,每个Web Server的内存均存储所有用户的Session,内存使用率很低。

另一个方案是采用粘滞会话(Sticky Session)机制,即反向代理在转发请求的时候,总是根据JSESSIONID的值判断,相同的JSESSIONID总是转发到固定的Web Server,但这需要反向代理的支持。

 

posted @ 2019-12-18 18:09  wenxuehai  阅读(6519)  评论(0编辑  收藏  举报
//右下角添加目录