zoukankan      html  css  js  c++  java
  • 【Distributed】分布式Session一致性问题

    一、概述

    1.1 什么是Session

      Session 是客户端与服务器通讯会话技术, 比如浏览器登陆、记录整个浏览会话信息 

    1.2 Session实现原理

      客户对向服务器端发送请求后,Session 创建在服务器端,返回Sessionid给客户端浏览器保存在本地,当下次发送请求的时候,在请求头中传递sessionId获取对应的从服务器上获取对应的Sesison

    1.3 Session常见问题

    Session 保证在那里?

      答案:存放在服务器上
      

    关闭浏览器Session会失效吗

      答案:不会消失
      

    服务器集群之后,Session产生的问题

      
      如果服务器产生了集群后,因为session是存放在服务器上,客户端会使用同一个Sessionid在多个不同的服务器上获取对应的Session,从而会导致Session不一致问题。
      

    1.4 Nginx

    Nginx配置负载均衡

    Nginx负载均衡提供上游服务器(真实业务逻辑访问的服务器),负载均衡、故障转移、失败重试、容错、健康检查等。
    当上游服务器(真实业务逻辑访问的服务器)发生故障时,可以转移到其他上游服务器(真实业务逻辑访问的服务器)。
    

    Upstream Server配置

    upstream 主要配置如下:
    IP地址和端口号:配置上游服务器的IP地址和端口
        ###定义上游服务器(需要被nginx真实代理访问的服务器) 默认是轮训机制
        upstream  backServer{
            server 127.0.0.1:8080;
            server 127.0.0.1:8081;
        }
        
    server {
        listen       80;
        server_name  www.itmayiedu.com;
        location / {
            ### 指定上游服务器负载均衡服务器
            proxy_pass http://backServer;
            index  index.html index.htm;
        }
    }
    

    负载均衡算法

    • 1、轮询(默认),每个请求按时间顺序逐一分配到不同的后端服务,如果后端某台服务器死机,自动剔除故障系统,使用户访问不受影响。
    • 2、weight(轮询权值),weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。或者仅仅为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。
      • 3、ip_hash,每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题。俗称IP绑定。
      • 4、fair(第三方),比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间 来分配请求,响应时间短的优先分配。Nginx本身不支持fair,如果需要这种调度算法,则必须安装upstream_fair模块。
      • 5、url_hash(第三方),按访问的URL的哈希结果来分配请求,使每个URL定向到一台后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身不支持url_hash,如果需要这种调度算法,则必须安装Nginx的hash软件包。

    二、Session 相关代码演示

    2.1 Controller

     @SpringBootApplication
        @RestController
        public class TestSessionController {
    
        // 创建session 会话
        @RequestMapping("/createSession")
        public String createSession(HttpServletRequest request, String nameValue) {
            HttpSession session = request.getSession();
            System.out.println("存入Session  sessionid:信息" + session.getId() + ",nameValue:" + nameValue);
            session.setAttribute("name", nameValue);
            return "success";
        }
    
        // 获取session 会话
        @RequestMapping("/getSession")
        public Object getSession(HttpServletRequest request) {
            HttpSession session = request.getSession();
            System.out.println("获取Session sessionid:信息" + session.getId());
            Object value = session.getAttribute("name");
            return value;
        }
    
        public static void main(String[] args) {
            SpringApplication.run(TestSessionController.class, args);
        }
    }
    

    2.2 TestSessionController

    @SpringBootApplication
    @RestController
    public class TestSessionController {
    @Value("${server.port}")
    private String serverPort;
    
    @RequestMapping("/")
    public String index() {
        return serverPort;
    }
    
    // 创建session 会话
    @RequestMapping("/createSession")
    public String createSession(HttpServletRequest request, String nameValue) {
        HttpSession session = request.getSession();
        System.out.println(
                "存入Session  sessionid:信息" + session.getId() + ",nameValue:" + nameValue + ",serverPort:" + serverPort);
        session.setAttribute("name", nameValue);
        return "success-" + serverPort;
    }
    
    // 获取session 会话
    @RequestMapping("/getSession")
    public Object getSession(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return serverPort + "-" + "没有找到对应的session值";
        }
        System.out.println("获取Session sessionid:信息" + session.getId() + "serverPort:" + serverPort);
        Object value = session.getAttribute("name");
        return serverPort + "-" + value;
    }
    
    public static void main(String[] args) {
        SpringApplication.run(TestSessionController.class, args);
    }
    }
    
    

    三、分布式Session一致性解决方案

    3.1 nginx或者haproxy实现IP绑定

    • 用Nginx 做的负载均衡可以添加ip_hash这个配置,用haproxy做的负载均衡可以用 balance source这个配置。从而使同一个ip的请求发到同一台服务器。

    3.2 利用数据库同步session

    3.3 使用Session集群存放Redis

      使用spring-session框架,底层实现原理是重写httpsession

    引入maven依赖

    <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.0.RELEASE</version>
            <relativePath /> <!-- lookup parent from repository -->
        </parent>
        <properties>
            <weixin-java-mp.version>2.8.0</weixin-java-mp.version>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.build.locales>zh_CN</project.build.locales>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <!-- <exclusions> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> 
                    <artifactId>jackson-databind</artifactId> </exclusion> </exclusions> -->
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.47</version>
            </dependency>
            <!-- Testing Dependencies -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <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>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
            </dependency>
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
            </dependency>
        </dependencies>
    

    YML配置信息

    server:
        port: 8080
        redis:
         hostname: 192.168.212.151
         port: 6379
         password: 123456  
        
    
    启动redis /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf 
    

    创建SessionConfig

    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;
    
        @Bean
        public JedisConnectionFactory connectionFactory() {
            JedisConnectionFactory connection = new JedisConnectionFactory();
            connection.setPort(Port);
            connection.setHostName(HostName);
            return connection;
        }
    }
    
    

    初始化Session

    //初始化Session配置
    public class SessionInitializer extends AbstractHttpSessionApplicationInitializer{
        public SessionInitializer() {
            super(SessionConfig.class);
        }
    }
    

    3.4 最靠谱的分布式Session解决方案

    基于令牌(Token)方式实现Session解决方案,因为Session本身就是分布式共享连接。

    Service

    @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;
        }
    }
    
    

      

  • 相关阅读:
    leetcode 155. Min Stack 、232. Implement Queue using Stacks 、225. Implement Stack using Queues
    leetcode 557. Reverse Words in a String III 、151. Reverse Words in a String
    leetcode 153. Find Minimum in Rotated Sorted Array 、154. Find Minimum in Rotated Sorted Array II 、33. Search in Rotated Sorted Array 、81. Search in Rotated Sorted Array II 、704. Binary Search
    leetcode 344. Reverse String 、541. Reverse String II 、796. Rotate String
    leetcode 162. Find Peak Element
    leetcode 88. Merge Sorted Array
    leetcode 74. Search a 2D Matrix 、240. Search a 2D Matrix II
    Android的API版本和名称对应关系
    spring 定时任务执行两次解决办法
    解析字符串为泛型的方法
  • 原文地址:https://www.cnblogs.com/haoworld/p/distributed-fen-bu-shisession-yi-zhi-xing-wen-ti.html
Copyright © 2011-2022 走看看