zoukankan      html  css  js  c++  java
  • 集群中Session共享解决方案分析

    一.为什么要Session共享

      Session存储在服务器的内存中,比如Java中,Session存放在JVM的中,Session也可以持久化到file,MySQL,redis等,SessionID存放在Cookie中。

      比如一个系统登录后,假如用户的请求通过Nginx被转发到tomcat1上,这时一些当前用户的信息放入session中,比如登录信息让用户一直处于登录状态。那么Nginx负载均衡后,可能用户刷新页面后重新跳转到了tomcat2,而tomcat2上没有Session,系统就会要求用户再次去登录,这样明显会给用户带来不好的体验。

    二.session共享解决方案(Java)

      1.tomcat可以直接配置

    <!-- 第1步:修改server.xml,在Host节点下添加如下Cluster节点 -->
    <!-- 用于Session复制 -->
    <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
        <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" />
        <Channel className="org.apache.catalina.tribes.group.GroupChannel">
            <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" 
                        port="45564" frequency="500" dropTime="3000" />
            <!-- 这里如果启动出现异常,则可以尝试把address中的"auto"改为"localhost" -->
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="4000" 
                      autoBind="100" selectorTimeout="5000" maxThreads="6" />
            <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
                <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" />
            </Sender>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor" />
        </Channel>
        <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter="" />
        <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
        <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" 
                  deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false" />
        <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" />
    </Cluster>
    <!-- 第2步:在web.xml中添加如下节点 -->
    <!-- 用于Session复制 -->
    <distributable/>

      2.使用token重写Session

        这里token是JSON Web Token,一般用它来替换掉Session实现数据共享(共享token?)

      3.使用Cookie的方式

        这个方式原理是将系统用户的Session信息加密、序列化后以Cookie的方式, 统一 种植在根域名下(如:.host.com),利用浏览器访问该根域名下的所有二级域名站点时,会传递与之域名对应的所有Cookie内容的特性,从而实现 用户的Cookie化Session 在多服务间的共享访问。

        这个方案的优点无需额外的服务器资源;缺点是由于受http协议头信心长度的限制,仅能够存储小部分的用户信息,同时Cookie化的 Session内容需要进行安全加解密(如:采用DES、RSA等进行明文加解密;再由MD5、SHA-1等算法进行防伪认证),另外它也会占用一定的带宽资源,因为浏览器会在请求当前域名下任何资源时将本地Cookie附加在http头中传递到服务器,最重要的是存在安全隐患。

      4.使用Nginx中的ip绑定方式

        这个只需要在Nginx中简单配置一句 ip_hash; 就可以了,但是该方式的缺点也很明显。具体可以参考的的上一篇博客 https://www.cnblogs.com/ywb-articles/p/10686673.html

      5.基于数据库的Session共享

        以为MySQL为例,每次将session数据存到数据库中。这个方案还是比较可行的,不少开发者使用了这种方式。但它的缺点在于Session的并发读写能力取决于MySQL数据库的性能,对数据库的压力大,同时需要自己实现Session淘汰逻辑,以便定时从数据表中更新、删除 Session记录,当并发过高时容易出现表锁,虽然可以选择行级锁的表引擎,但很多时候这个方案不是最优方案。

      6.使用redis的方式(Spring Session + redis实现,也可以是其他NoSQL)(推荐)

         该方式配置简单,数据安全且稳定,效率高,被普遍使用。下面是简单的案例

          1.创建Spring Boot项目,导入下列相关的依赖,还有web模块的依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.webjars</groupId>
                <artifactId>bootstrap</artifactId>
                <version>4.3.1</version>
            </dependency>
    
            <!-- springboot - Redis -->
            <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
            <!--spring session 与redis应用基本环境配置,需要开启redis后才可以使用,不然启动Spring boot会报错 -->
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-data-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
            </dependency>

          2.在主配置类中开启RedisHttpSession

    @EnableRedisHttpSession

          3.编写控制器

    @Controller
    public class MyController {
    @RequestMapping("/set")
    public String setSession (HttpServletRequest request){
    String name = (String)request.getSession().getAttribute("name");
    if(name !=null){//如果session存在了,直接跳转到成功页面,否则添加session并跳转到首页
    return "success";
    }
    request.getSession().setAttribute("name","zhangsan");
    return "index";
    }

    @RequestMapping("/get")
    public String getSession (HttpServletRequest request){
    String name = (String)request.getSession().getAttribute("name");
    System.out.println(name);
    if(name != null){
    return "success";
    }else{
    return "error";
    }
    }
    }

        4.在application.properties中配置

    server.port=8080
    #远程连接redis的配置 spring.redis.host=192.168.2.130 spring.redis.port=6379 spring.redis.database=0 spring.redis.password=123456 spring.redis.timeout=5000 spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=0 spring.redis.pool.max-active=8 spring.redis.max-wait=-1

        5.在template中写几个页面方便测试,这几个页面中每个页面放一句话就可以了,如下图

          

        6.运行Spring Boot项目,再修改配置文件中server.port=8081,运行Spring Boot项目(同时用两个不同端口的tomcat运行项目)

        7.测试:

          首先查看session是否存在,如下图所示,发现session都是null,跳转到错误页面

            

             

          在8080中第一次set,发现session为null后跳转到index页面,如下图

            

          刷新后,因为session存在,所以跳转到了成功页面

            

          再在8081中get,发现也跳转到了成功页面,说明8081的tomcat获取到了session

            

          最后,我们可以在redis中查看,发现多了一个session 数据包,如下图

            

          说明Spring Session + redis成功解决集群中session共享的问题

           注意:在redis中删除这个数据包,发现8080和8081都get不到session了,说明session没有存放在JVM中了,而是转存放在redis中了。

  • 相关阅读:
    Python采用struct处理二进制
    OVS处理upcall流程分析
    mybatis在CRUD
    leetcode先刷_Valid Sudoku
    [TS] Implement a doubly linked list in TypeScript
    [TS] Implement a singly linked list in TypeScript
    [Python] The get() method on Python dicts and its "default" arg
    [Javascript AST] 4. Continue: Report ESLint error
    [RxJS] Learn How To Use RxJS 5.5 Beta 2
    [NPM] Update published npm packages using np
  • 原文地址:https://www.cnblogs.com/ywb-articles/p/10780803.html
Copyright © 2011-2022 走看看