分布式Session一致性解决方案
2018-08-14 12:41
447 查看
1、session的作用?
服务器为每个用户创建一个会话,存储用户的相关信息,以便多次请求能够定位到同一个上下文。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在 整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。
2、分布式session是什么?
单服务器web应用中,session只会存储在该服务器中,而随着日益增长的用户量,单服务节点无法应对大量用户的 访问,我们开始考虑适用服务器集群,例如采用nginx做负载均衡反向代理,那么就会出现用户每次访问都会轮训到 不同的web应用服务器节点,从而导致获取不到Session的情况,这就是我们谈到的分布式session一致性问题。
如下案例
首先我们创建一个项目名称为distributed-session的springboot项目如下
书写如下接口
启动类:
application.yml配置如下:
分别启动两台服务器,本地ip为192.168.100.1。即现在启动了两台服务分别是
192.168.100.1:8080和192.168.100.1:8081
接下来我们配置nginx反向代理负载均衡,我的nginx服务器是在一台ip为192.168.100.131的虚拟机上安装的,本机ip为192.168.100.1,所以我在本地配置了一个域名指向这台服务器的ip。如下:
接下里我在ip为192.168.100.131的虚拟机配置了反向代理如下:
接下来我们创建第一个session:
http://www.itchao.cn/user/createSession?name=user1
发现在8081服务器上创建了session
接着我们在创建第二个session
http://www.itchao.cn/user/createSession?name=user2
发现这时候轮训到8080服务器了
接下来我们来获取这个session
http://www.itchao.cn/user/getSession
发现轮训到8081服务器上没有获取到对应的session
我们再来获取一次Session
发现在8080服务器上获取到了刚才创建session,这是为什么呢?
原因很简单由于这段代码没有设置创建session的时候为false
requset.getSession(false)代表获取session,如果没有不创建,而默认requset.getSession()等同与requset.getSession(true)代表每次获取session如果获取不到创建一个新的session,故此我们在刚才轮训到8081服务器上的时候新建了一个session,把我们设置的name=user1给覆盖掉了,所以我们无法获取到。
每次创建的session,和下一次访问的可能不在一台服务器上,所以不能获取达到session共享从而导致session一致性问题。
那我们应该如何解决这个问题呢?
1、通过数据库存储session信息,每次从数据查询。这种方式会导致数据库压力巨大。不适用
2、通过客户端Cookie写入到本地,这样很不安全,不可采取
3、通过nginx的hash一致性做ip绑定,这样失去了灵活性
4、使用tomcat内部通信机制同步数据通信,这样开销较大,不适合大型项目
5、使用spring-session框架来解决,底层采用重写httpclient保证数据共享
6、使用token令牌代替session功能,把数据存放在redis中,每次从redis中获取数据
接下来我们说说如何使用spring-session框架解决分布式session一致性问题:
首先引入一下依赖
编写如下代码
配置文件中添加如下配置:
需要依赖redis,请安装redis
接下来我们再次启动8080和8081服务器,测试发现
完美解决session共享问题。
利用token令牌和redis共享分布式集群方式替换Session功能。大概思路原理如下:
用户登录后,生成一个唯一令牌,令牌作为redis的key,用户信息作为key对应的value存入redis中,给设置过期时间为30分钟,然后把令牌返回给客户端,客户端保存令牌到本地Cookie中,以后用在获取用户信息的时候直接发送一个请求到后端,在请求头部传入令牌,后端通过request.getHeader("Authorization");获取到令牌,然后用令牌去redis查询对应得用户信息,如果没有查询到那么代表用户Session失效或者用户未登陆。如果查询到即返回用户信息。
服务器为每个用户创建一个会话,存储用户的相关信息,以便多次请求能够定位到同一个上下文。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在 整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。
2、分布式session是什么?
单服务器web应用中,session只会存储在该服务器中,而随着日益增长的用户量,单服务节点无法应对大量用户的 访问,我们开始考虑适用服务器集群,例如采用nginx做负载均衡反向代理,那么就会出现用户每次访问都会轮训到 不同的web应用服务器节点,从而导致获取不到Session的情况,这就是我们谈到的分布式session一致性问题。
如下案例
首先我们创建一个项目名称为distributed-session的springboot项目如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.itchao</groupId> <artifactId>distributed-session</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <!--SpringBoot WEB组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--lombok插件 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--common-lang3工具包 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> </dependencies> </project>
书写如下接口
package cn.itchao.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import lombok.extern.slf4j.Slf4j; @RestController @RequestMapping("/user") @Slf4j public class UserController { @Value("${server.port}") private String port; @RequestMapping("/createSession") public String createSession(HttpServletRequest request,String name){ HttpSession session = request.getSession(); session.setAttribute("name", name); log.info("port:"+port+",sessonId:"+session.getId()); return "创建Session成功-对应的服务器端口号为:"+port; } @RequestMapping("/getSession") public String getSession(HttpServletRequest request){ HttpSession session = request.getSession(false); if(session==null){ return "没有获取到Session"+"-服务器端口号为:"+port; } log.info("port:"+port+",sessonId:"+session.getId()); String name = (String) session.getAttribute("name"); return "获取Session成功:"+name+"-对应的服务器端口号为:"+port; } }
启动类:
package cn.itchao; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SessionApp { public static void main(String[] args) throws Exception { SpringApplication.run(SessionApp.class, args); } }
application.yml配置如下:
server: port: 8080
server: port: 8081
分别启动两台服务器,本地ip为192.168.100.1。即现在启动了两台服务分别是
192.168.100.1:8080和192.168.100.1:8081
接下来我们配置nginx反向代理负载均衡,我的nginx服务器是在一台ip为192.168.100.131的虚拟机上安装的,本机ip为192.168.100.1,所以我在本地配置了一个域名指向这台服务器的ip。如下:
接下里我在ip为192.168.100.131的虚拟机配置了反向代理如下:
接下来我们创建第一个session:
http://www.itchao.cn/user/createSession?name=user1
发现在8081服务器上创建了session
接着我们在创建第二个session
http://www.itchao.cn/user/createSession?name=user2
发现这时候轮训到8080服务器了
接下来我们来获取这个session
http://www.itchao.cn/user/getSession
发现轮训到8081服务器上没有获取到对应的session
我们再来获取一次Session
发现在8080服务器上获取到了刚才创建session,这是为什么呢?
原因很简单由于这段代码没有设置创建session的时候为false
requset.getSession(false)代表获取session,如果没有不创建,而默认requset.getSession()等同与requset.getSession(true)代表每次获取session如果获取不到创建一个新的session,故此我们在刚才轮训到8081服务器上的时候新建了一个session,把我们设置的name=user1给覆盖掉了,所以我们无法获取到。
每次创建的session,和下一次访问的可能不在一台服务器上,所以不能获取达到session共享从而导致session一致性问题。
那我们应该如何解决这个问题呢?
1、通过数据库存储session信息,每次从数据查询。这种方式会导致数据库压力巨大。不适用
2、通过客户端Cookie写入到本地,这样很不安全,不可采取
3、通过nginx的hash一致性做ip绑定,这样失去了灵活性
4、使用tomcat内部通信机制同步数据通信,这样开销较大,不适合大型项目
5、使用spring-session框架来解决,底层采用重写httpclient保证数据共享
6、使用token令牌代替session功能,把数据存放在redis中,每次从redis中获取数据
接下来我们说说如何使用spring-session框架解决分布式session一致性问题:
首先引入一下依赖
<!-- springboot - Redis --> <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>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
编写如下代码
package config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; public class SessionConfig { // 冒号后的值为没有配置文件时,自动动装载的默认值 @Value("${redis.hostname:localhost}") String host; @Value("${redis.port:6379}") int port; @Value("${redis.password:123456}") String password; @Bean public JedisConnectionFactory connectionFactory() { RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration(); standaloneConfig.setHostName(host); standaloneConfig.setPort(port); standaloneConfig.setPassword(RedisPassword.of(password)); return new JedisConnectionFactory(standaloneConfig); } }
package cn.itchao.session; import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer; import config.SessionConfig; //初始化Session配置 public class SessionInitializer extends AbstractHttpSessionApplicationInitializer { public SessionInitializer() { super(SessionConfig.class); } }
配置文件中添加如下配置:
server: port: 8080
spring:
redis:
database: 0
host: 127.0.0.1
port: 6379
password: 123456
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 8
timeout: 0
session:
store-type: redis
需要依赖redis,请安装redis
接下来我们再次启动8080和8081服务器,测试发现
完美解决session共享问题。
利用token令牌和redis共享分布式集群方式替换Session功能。大概思路原理如下:
用户登录后,生成一个唯一令牌,令牌作为redis的key,用户信息作为key对应的value存入redis中,给设置过期时间为30分钟,然后把令牌返回给客户端,客户端保存令牌到本地Cookie中,以后用在获取用户信息的时候直接发送一个请求到后端,在请求头部传入令牌,后端通过request.getHeader("Authorization");获取到令牌,然后用令牌去redis查询对应得用户信息,如果没有查询到那么代表用户Session失效或者用户未登陆。如果查询到即返回用户信息。
相关文章推荐
- 分布式架构中一致性解决方案——Zookeeper集群搭建
- 分布式系统事务一致性解决方案
- 分布式事务一致性解决方案
- 分布式系统事务一致性解决方案(转)
- 分布式事务下的交易一致性解决方案(逻辑代码结构)
- 【系统服务】分布式系统事务一致性解决方案
- Apache shiro集群实现 (六)分布式集群系统下的高可用session解决方案---Session共享
- 大话分布式环境接口幂等性与一致性解决方案
- Apache shiro集群实现 (五)分布式集群系统下的高可用session解决方案
- Apache shiro集群实现 (六)分布式集群系统下的高可用session解决方案---Session共享
- 分布式系统事务一致性解决方案
- 设计----【分布式事务】分布式系统事务一致性解决方案
- 分布式事务及分布式系统一致性解决方案
- 分布式系统事务一致性解决方案
- Memcahed分布式缓存服务替换Session解决方案——简单介绍[转]
- 分布式系统--事务一致性解决方案
- 消息中间件(一)分布式系统事务一致性解决方案大对比,谁最好使?
- 分布式系统事务一致性解决方案大对比
- 分布式系统事务一致性解决方案
- Apache shiro集群实现 (六)分布式集群系统下的高可用session解决方案---Session共享