zoukankan      html  css  js  c++  java
  • 分布式session一致性问题

    1,什么是session

    session 是客户端与服务器通讯会话技术,比如用户登陆,会将登陆之后,将用户信息存入在session中

    2,session 的原理

      

    3,简单的session 例子

    @RestController
    public class IndexController {
    
        @RequestMapping("/createSession")
        public String createSession(HttpServletRequest request, String username) {
            HttpSession session = request.getSession();
            session.setAttribute("username", username);
            return "success";
        }
    
        @RequestMapping("/getSession")
        public String getSession(HttpServletRequest request) {
            HttpSession session = request.getSession();
            String username = (String) session.getAttribute("username");
            System.out.println("username: " + username);
            String id = session.getId();
            System.out.println("sessionid " + id);
            return "username: " + username + "=====" + "sessionid: " + id;
        }
        
        @RequestMapping("/baseDecode")
        private String base64Decode(String base64Value) {
            try {
                byte[] decodedCookieBytes = Base64.getDecoder().decode(base64Value);
                return new String(decodedCookieBytes);
            }
            catch (Exception e) {
                return null;
            }
        }
        
        
    }

    服务器端创建了一个session,sessionid 是 cdd51f6c-e2ab-490b-a4f8-291f184913b3 通过 base64 加密之后(Y2RkNTFmNmMtZTJhYi00OTBiLWE0ZjgtMjkxZjE4NDkxM2Iz)传给客户端,存入到http request headers ,以便下一次请求

    getSession 的时候也是将Y2RkNTFmNmMtZTJhYi00OTBiLWE0ZjgtMjkxZjE4NDkxM2Iz 在请求头中传入,然后服务器解密,找到对应的session

    4,分布式session 一致性问题

    在分布式集群环境中,sessionid 是存在客户端中,session 是存在服务器上,因为是分布式集群,所以会存在客户端的sessionid 和 session 不一致。

    5,分布式session一致性问题的解决方案

        解决方案1:nginx:ip_hash 负载均衡算法设置

                            详解:通过nginx负载均衡算法中的 ip_hash ,也就是ip 绑定方式,让每个客户端的和服务器进行了绑定,A 客户端访问了1号服务器,后面A 客户端发起的请求,都会分发到1号服务器了。

                            缺点:没有了负载均衡

                            

      # 配置上游服务器
        upstream  backServer{
            server 127.0.0.1:8080;
            server 127.0.0.1:8082;
            ip_hash;
        }
        
        server {
           listen       80;
           server_name  www.baiyue.com;
    
           location / {
               proxy_pass http://backServer;
               index  index.html index.htm;
           }
        }

          

        解决方案2:使用spring-session框架,底层实现原理是重写httpsession,推荐

              

            <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>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
            </dependency>
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
    
    //这个类用配置redis服务器的连接
    //maxInactiveIntervalInSeconds为SpringSession的过期时间(单位:秒)
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
    public class SessionConfig {
    
        // 冒号后的值为没有配置文件时,制动装载的默认值
        @Value("${redis.hostname:localhost}")
        String HostName;
        @Value("${redis.port:6379}")
        int Port;
        @Value("${redis.password}")
        String password;
    
        @Bean
        public JedisConnectionFactory connectionFactory() {
            JedisConnectionFactory connection = new JedisConnectionFactory();
            connection.setPort(Port);
            connection.setHostName(HostName);
            connection.setPassword(password);
            return connection;
        }
    }
    import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
    
    //初始化Session配置
    public class SessionInitializer extends AbstractHttpSessionApplicationInitializer{
      public SessionInitializer() {
          super(SessionConfig.class);
      }
    }

    application.yml

    server:
      port: 8080
    spring:
      redis:
        database: 0
        host: 192.168.178.110
        port: 6379
        password: 123
        jedis:
          pool:
            max-active: 8
            max-wait: -1
            max-idle: 8
            min-idle: 0
        timeout: 10000
    redis:
     hostname: 192.168.178.110
     port: 6379
     password: 123

    启动8080 和 8081 模拟集群,8080 创建的session,8081 也可以获取到了,实现了session 的共享,实际上是将session 存到了redis 上

     

    解决方案3:基于Token(令牌)方式,推荐

                            其实就是通过就是token缓存在redis 上,因为redis 在服务器集群的时候 分布式缓存可以共享

                            每次都是通过token 来存,再通过token 来获取

                            

    @Service
    public class TokenService {
        @Autowired
        private RedisService redisService;
    
        // 新增 返回token
        public String put(Object object) {
            String token = getToken();
            redisService.setString(token, object);
            return token;
        }
    
        // 获取信息
        public String get(String token) {
            String reuslt = redisService.getString(token);
            return reuslt;
        }
    
        public String getToken() {
            return UUID.randomUUID().toString();
        }
    
    }
    
    TokenController 
    
    @RestController
    public class TokenController {
        @Autowired
        private TokenService tokenService;
        @Value("${server.port}")
        private String serverPort;
    
        @RequestMapping("/put")
        public String put(String nameValue) {
            String token = tokenService.put(nameValue);
            return token + "-" + serverPort;
        }
    
        @RequestMapping("/get")
        public String get(String token) {
            String value = tokenService.get(token);
            return value + "-" + serverPort;
        }
    
    }
  • 相关阅读:
    JavaSE 基础 第51节 定义自己的异常
    JavaSE 基础 第50节 Java中的异常链
    JavaSE 基础 第49节 手动抛出异常
    JavaSE 基础 第48节 Java中的异常声明
    JavaSE 基础 第47节 获取异常信息
    JavaSE 基础 第46节 异常的分类
    JavaSE 基础 第45节Java异常快速入门
    JavaSE 基础 第44节 引用外部类的对象
    JavaSE 基础 第43节 静态内部类
    通用爬虫
  • 原文地址:https://www.cnblogs.com/pickKnow/p/11339600.html
Copyright © 2011-2022 走看看