分布式Session一致性问题

Session

概念

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

Session实现原理

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

注意Session是保存在服务器端的,浏览器关闭不会到时session失效的。

分布式Session一致性

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

演示一致性问题

1.修改hosts文件

127.0.0.1   www.lun.com

2.修改Nginx配置文件,实现负载均衡

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

3.后端相关代码

实现获取session,建立session操作

@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) {
		// 默认 创建一个session,
		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();
		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);
	}
}

此时访问www.lun.com就会发现负载均衡轮训效果

建立session,存入session名称

此时已在8081端口号建立了session,但是下一步得到session的时候,会发生问题

8081和8080端口号的服务都没有改session了,这是为什么呢?

在request获取session的时候,可以将参数设置为false,使客户端查不到对应的session的时候,不会直接创建新的session。

// 获取session 会话
	@RequestMapping("/getSession")
	public Object getSession(HttpServletRequest request) {
		// 设置为true 情况下的时候,客户端使用对应的sessionid 查询不到对应的sesison 会直接创建一个新的session
		// 设置为false 情况下的时候,客户端使用对应的sessionid 查询不到对应的sesison 不 会直接创建一个新的session
		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;
	}

这样就能解决sessionId被覆盖的问题。以上就是分布式session一致性问题。

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

  • 可以直接使用cookie替代Session(不靠谱)
  • 使用Nginx(反向代理)进行ip绑定,同一个ip只能在指定的同一个机器上访问(失去了负载均衡的效果)
  • 使用数据库同步session(效率不高)
  • tomcat内置支持对session同步(不推荐),同步可能会产生延迟
  • 使用Spring-Session框架,相当于把session值缓存到redis中
  • 使用token替代session功能,token最终存放在redis中,redis支持分布式共享(最靠谱)

使用Spring-Session框架

1、pom文件引入

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

2、配置文件

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、后端相关代码

1.建立sessonConfig类,用于配置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;
	}
}

2.初始化session配置

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

这样就可以解决分布式session一致性的问题。简单来说,底层就是重写HTTPSession类,将我们的session值存放到redis中。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值