大型网站分布式架构(九)—— 从Cookie机制到会话Session再到集群中Session的场景

Cookie机制

  • cookie是浏览器用来存储少量数据的一种机制,数据以”key/value“形式存储,浏览器发送http请求时自动附带cookie信息

Session会话机制

  • HTTP请求是无状态的,即HTTP协议并不能识别出上一个HTTP请求和下一个HTTP请求是否来自同一个用户
  • 可以通过维持一个会话来判定用户:浏览器第一次请求服务器,服务器创建一个会话,并将会话的id作为响应的一部分发送给浏览器,浏览器存储会话id,并在后续第二次和第三次请求中带上会话id,服务器取得请求中的会话id就知道是不是同一个用户了
  • 但是每次请求都携带会话id显然不靠谱,这时就可以通过cookie机制就实践会话id的传递

Tomcat下的Cookie和HttpServletSession

浏览器第一次访问Tomcat时,Tomcat会创建一个HttpServletSession会话对象,同时创建一个名为JSESSIONID的Cookie对象,该cookie存储着当前HttpServletSession会话对象的id,可以通过这个id找到当前会话,从而实现了用户状态的保持。而Session对象存活时间默认等于30分钟或者浏览器的打开时间,在这段时间内,我们可以获取Session对象中存储的任何信息。这就是Tomcat下Cookie和Session机制

更新项目操作Session对象

修改IndexController如下:

package com.zaomianbao.appdemo;

import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;

@Controller
public class IndexController {

    @RequestMapping("/")
    public String index(HttpServletRequest request){
        try {
            System.out.println(new Date());
            InetAddress addr = InetAddress.getLocalHost();
            String ip=addr.getHostAddress().toString(); //获取本机ip
            String host=addr.getHostName().toString(); //获取本机计算机名称
            System.out.println(ip + " " + host);
            request.setAttribute("ip",ip);
            request.setAttribute("host",host);
            HttpSession session = request.getSession();
            String random = request.getParameter("random");
            if(StringUtils.hasText(random)){
                session.setAttribute("random",random);
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return "index";
    }
}

修改index.html如下

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>欢迎进入枣面包的面包坊</title>
    <script type="text/javascript" src="js/nothing.js"></script>
</head>
<body>
    <h1>欢迎进入枣面包的面包坊</h1>
    <img src="img/photo.jpg"><br/>

    主机名:<span th:text="${host}" ></span><br/>
    ip:<span th:text="${ip}" ></span><br/>
    session中的random值:<span th:text="${session.random}" ></span>
</body>
</html>

部署项目到Tomcat集群

[root@centos6-1 webapps]# rz
rz waiting to receive.
Starting zmodem transfer.  Press Ctrl+C to cancel.
Transferring appdemo.war...
appdemo.war was skipped

[root@centos6-1 webapps]# scp -r appdemo.war root@centos6-2:$PWD
appdemo.war                                                                                                 100%   17MB  16.9MB/s   00:00    
[root@centos6-1 webapps]# scp -r appdemo.war root@centos6-3:$PWD
appdemo.war                                                                                                 100%   17MB  16.9MB/s   00:00    
[root@centos6-1 webapps]# 

启动Tomcat集群

[root@centos6-1 bin]# ./startup.sh  
[root@centos6-2 bin]# ./startup.sh 
[root@centos6-3 bin]# ./startup.sh 

浏览器访问Nginx

上一章讲到的Nginx集成Tomcat集群:大型网站分布式架构(八)—— Tomcat集群横向拓展 + Nginx负载均衡

1.直接访问Nginx:192.168.214.150/appdemo/

这里写图片描述
因为是初次访问,session中并未设置random值,所以取不到值

2.携带random参数访问:192.168.214.150/appdemo/?random=zaomianbao

这里写图片描述
发现random存入了session中,而此时处理这个请求的Tomcat是在centos6-1上的Tomcat1

3.连续直接访问Nginx:192.168.214.150/appdemo/

这里写图片描述
这里写图片描述
这里写图片描述
发现我们之前存入session中的random的值没有了,而博主并没有关闭浏览器,且是在连续请求的情况下,session也不可能过期。就连当请求分配给Tomcat1处理时也取不到random的值。

Session去哪了

其道理很简单:

  • 当第一次打开浏览器请求并携带random参数时,请求分配给了Tomcat1,因为是第一次请求,这时Tomcat1响应回来时创建了名为JSESSIONID的cookie并携带回来给了浏览器
  • 当浏览器连续第二次请求时,请求分给Tomcat2处理了,并且携带那个JSESSIONID的cookie给了Tomcat2,不管此时是否携带random参数,当在接口请求响应回来时,Tomcat2发现从名为JESESSIONID的cookie中获得的会话id并不能找到对应的HttpSession对象,所以Tomcat2重新创建了一个HttpSession,并将新的会话id放入名为JESESSION的cookie中返回给了浏览器
  • 当浏览器连续第二次请求时,请求又分给Tomcat3处理了,又识别不了,又重新创建,又产生新的会话id返回给浏览器,不断重复此操作。

所以,问题在于Tomcat找不到Session对象,因为Session对象都存放在各自的Tomcat容器中,所以集群中的3台Tomcat容器找不到会话id对应的会话Session对象。所以得出结论:因为集群没有共享Session,所以导致会话的不断刷新,从而不能保持会话的状态。

总结

之前的Nginx+Tomcat集群的架构解决了高并发的问题,但又带来了Session会话状态的问题。下一章博主将通过使用Redis实现集群中的Session的共享