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;
        }
    
    }
  • 相关阅读:
    ABAP TCode
    SAP 常用的事务代码
    SAP FI TCode
    Little Tutorials的一篇文章断言:UML(统一建模语言)正在死亡:
    SAP PP TCode
    [ZT]解密中国IT人十大职业现状
    User Exit Query
    SAP客户端多语言设置
    一个女CIO的诞生
    DIY防奸手册之 主流硬盘型号解惑篇
  • 原文地址:https://www.cnblogs.com/pickKnow/p/11339600.html
Copyright © 2011-2022 走看看