分布式Session一致性问题解决方案

1、首先先说下基本概念

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

实现原理:客户对向服务器端发送请求后,Session 创建在服务器端,返回Sessionid给客户端浏览器保存在本地,当下次发送请求的时候,在请求头中传递sessionId获取对应的从服务器上获取对应的Session。Session 存放在服务器上,关闭浏览器是不会影响到session的。

2、session不一致性产生的原因

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

3、解决方案

3.1 nginx或者haproxy实现IP绑定(不推荐  弊端很大,相当于负载均衡用处不大)

    用Nginx 做的负载均衡可以添加ip_hash这个配置,

    用haproxy做的负载均衡可以用 balance source这个配置。

   从而使同一个ip的请求发到同一台服务器。

3.2 利用数据库同步session (不推荐 效率不高)

3.3 tomcat内置支持session同步(不推荐)同步可能会产生延迟

3.4 使用缓存框架缓存session,将Session集群存放Redis

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

3.5 可以使用token 替代 session功能

现在说下 spring-session框架用法实现:

4、使用spring-session实现session一致性

1、在pom.xml中添加依赖信息

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

2、yml文件中配置redis信息

server:
  port: 8081
spring:
  redis:
    database: 0
    host: 192.168.212.151
    port: 6379
    password: 123456
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0
    timeout: 10000
redis:
 hostname: 192.168.212.151
 port: 6379
 password: 123456

 3、写redis服务器的连接配置类


//这个类用配置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;
	}
}

4、初始化Session配置

 

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

开启多个springboot启动tomcat, 使用nginx配置反向代理,集群tomcat, 然后进行测试,即可。访问不同的端口的tomcat,获取到的 session值 是否都是一样,是否都能获取到正确的session。  可以查看redis上面,是否存放在了 redis上面。

 

5、使用token实现session一致性

@Service
public class TokenService {
	@Autowired
	private RedisService redisService;

	// 新增 返回token
	public String put(Object object) {
        if(object== null) {
	        return null;
	    }
		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;
	}

}

@Component
public class RedisService {

	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	// public void set(String key, Object object, Long time) {
	// stringRedisTemplate.opsForValue();
	// // 存放String 类型
	// if (object instanceof String) {
	// setString(key, object);
	// }
	// // 存放 set类型
	// if (object instanceof Set) {
	// setSet(key, object);
	// }
	// // 设置有效期 以秒为单位
	// stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
	// }
	//
	public void setString(String key, Object object) {
		// 开启事务权限
		// stringRedisTemplate.setEnableTransactionSupport(true);
		try {
			// 开启事务 begin
			// stringRedisTemplate.multi();
			String value = (String) object;
			stringRedisTemplate.opsForValue().set(key, value);
			System.out.println("存入完毕,马上开始提交redis事务");
			// 提交事务
			// stringRedisTemplate.exec();
		} catch (Exception e) {
			// 需要回滚事务
			// stringRedisTemplate.discard();
		}
	}

	public void setSet(String key, Object object) {
		Set<String> value = (Set<String>) object;
		for (String oj : value) {
			stringRedisTemplate.opsForSet().add(key, oj);
		}
	}

	public String getString(String key) {
		return stringRedisTemplate.opsForValue().get(key);
	}

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值