分布式 Session原理 分布式session问题_redis

一、什么是分布式session问题

a、什么是session

session是一种会话技术,我们知道http是无状态协议的,就是这次连接传输数据后,下次连接服务器是不知道这次的请求是谁的,因此我们要做一个标记,让服务器知道每次请求是哪个(客户端)浏览器发出的,就是请求的时候服务器会创建一个session把session的值保存在服务器,把sessionID返回给浏览器,请求的时候把sessionID放在请求头中,这样服务器解析之后就能发现是哪个浏览器发来的请求

b、session存放在什么地方,会引起什么问题

session是存在服务器的,只是把sessionID返回给浏览器。这样我们把浏览器关掉,session也不会实现,但是只是丢失了sessionID,这样也是访问不到的。

c、session的工作原理

session是由服务器创建的,存放在服务器中,把sessionID返回给浏览器,请求的时候,每次请求把sessionID就到请求头中,服务器解析以后就知道是哪个浏览器

e、分布式session是什么

我们知道session是保存在服务器的,这样当我们的项目做了负载均衡以后,如果在session中存了数据,那么就有可能有有些项目取不到session中的数据,这就是分布式session问题

二、服务做了集群一般是怎么样做的

如果是一般的springboot项目,那么只要改下端口号启动几个,然后用nginx统一做反向代理。

springcloud微服务项目,那么这个时候,可以使用ribbon本地负载均衡。

nginx负载均衡和ribbon做负载均衡的区别:

nginx做负载均衡是服务器端的负载均衡,统一访问一个地址,根据负载均衡算法访问决定访问那一个服务器。

ribbon负载均衡,这是本地负载均衡(也可以说是客户端负载均衡),把提供服务的看客户端地址都缓存记录下来,根据本地的算法实现负载均衡。

三、分布式session解决办法

a、项目中用到了session,那么使用springsession解决分布式问题

使用springsession的原理:把项目中的session统一存在redis中,所有的参与集群的项目都在redis中取,这样就不不会出现明明在session中设了值但取不到值的情况。

maven依赖

<groupId>com.junlaninfo</groupId>
<artifactId>solveSession</artifactId>
<version>1.0-SNAPSHOT</version>

<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>

 properties 文件

server.port=8080
redis.hostname=127.0.0.1
redis.port=6379
redis.password=

 redis配置

package com.junlaninfo.config;

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;
   }
}
package com.junlaninfo.config;

import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;

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

项目启动

package com.junlaninfo;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class TestSessionController {

   // 创建session 会话
    @RequestMapping("/createSession")
   public String createSession(HttpServletRequest request, String nameValue) {
      HttpSession session = request.getSession(false);
      String id = session.getId();
      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(false);
      String id = session.getId();
      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);
   }
}

 

只要加入springsession的依赖,配置好redis,那么就可以解决分布式session一致性的问题,不用在原来的项目中做任何改变

 

 

使用nginx做负载均衡配置

分布式 Session原理 分布式session问题_分布式 Session原理_02

 

启动项目,分别用8080和8081启动,这样就可以看到解决分布式session问题的效果

 

创建session的注意点:

HttpSession session = request.getSession(false);

这里可以传入一个布尔类型的变量,ture的话表示没有这个sessionID对应的session那么就新创建一个。

如果是false,那么没有这个sessionID对应的session,那么就直接返回失败,不会新创建session

 

b、使用token代替session,这样就不会引起分布式session问题

这里我们需要提一个问题,项目中使用到了session,那么即使使用springsession解决分布式session一致性的问题,只要你换了一个浏览器操作,那么同样因为sessionID丢失的问题,也同样获取不到值,为了彻底解决分布式session的问题,那么我们可以用token代替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();
}
 
}

 

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

 

 

c、项目中使用到了session,使用nginx的ip绑定解决分布式session的问题

分布式 Session原理 分布式session问题_服务器_03

分布式 Session原理 分布式session问题_spring_04

 

代码:https://github.com/xuexionghui/solveSession.git