分布式Session一致性问题

什么是Session

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

Session实现原理

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

 

Session常见问题

Session 存放在服务器上

关闭浏览器Session不会消失

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

服务集群会产生那些问题

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

分布式Session一致性解决方案有那些

答:

1,使用cookie代替session,缺点是不安全,不推荐

2,使用Nginx的IP绑定,缺点是没有负载均衡,不推荐。

3,使用数据库,但是效率不高,不推荐。

4,Tomcat中内置session的复制,缺点是有延迟,不推荐。

5,使用Spring-session框架,缓存到Redis中。推荐。

6,使用token代替session。推荐。

使用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>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <maimClass>com.meiteedu.WxMpApplication</maimClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>

            </plugin>
        </plugins>
    </build>
YML配置信息
server:
  port: 8080
redis:
 hostname: 192.168.212.151
 port: 6379
 password: 123456

  

创建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);
    }
}

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

 

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

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

}

 

posted @ 2019-01-06 09:25  AndyMoe  阅读(280)  评论(0)    收藏  举报