分布式中session共享的解决方案:spring-session
Session是客户端与服务器通讯会话跟踪技术,是服务器与客户端保持整个通讯的会话基本信息。客户端在第一次访问服务器的时候,服务端会响应一个sessionId并且将它存入到本地的Cookie中,在之后的访问会将Cookie中的sessionId放入到请求头中去访问服务器,如果通过这个sessionId没有找到对应的数据,那么服务器就会创建一个新的sessioinId并且响应给客户端。分布式Session的一致性说白了就是服务器集群Session共享的问题。
分布式中Session存在的共享问题
假设客户端第一次访问服务A,服务A响应返回了一个sessionId并且存入了本地Cookie中。第二次不访问服务A了,转去访问服务B。因为客户端中的Cookie中已经存有了sessionId,所以访问服务B的时候,会将sessionId加入到请求头中,而服务B因为通过sessionId没有找到相对应的数据,因此它就会创建一个新的sessionId并且响应返回给客户端。这样就造成了不能共享Session的问题。
分布式中Session共享问题的解决方案
1.根据Cookie来完成(不安全)。
2.使用Nginx的IP绑定策略,同一个IP只能在指定的同一个机器访问(不支持负载均衡)。
3.利用数据库同步Session(效率不高)。
4.使用Tomcat内置的Session同步机制(同步可能会产生延迟)。
5.使用Token代替Session。
6.使用Spring-Session以及集成好的解决方案,存放在Redis中。
项目实例场景还原
启动两个Spring Boot项目,端口号分别是8081,8182。
在两个项目中分别创建SessionSharedController类。
@RestController
public class SessionSharedController {
@Value("${server.port}")
private Integer projectPort; // 项目端口 @RequestMapping("/createSession")
public String createSession(HttpSession session, String name) {
session.setAttribute("name", name);
return "【当前项目端口:" + projectPort + "】 【当前sessionId :" + session.getId() + "】";
} @RequestMapping("/getSession")
public String getSession(HttpSession session) {
return "【当前项目端口:" + projectPort + "】 【当前sessionId :" + session.getId()
+ "】 【获取的姓名:" + session.getAttribute("name") + "】";
}
}
使用Nginx集群,通过修改nginx.conf配置文件使之支持轮询策略(默认)的负载均衡。
# 开启轮询策略(默认)的负载均衡
upstream balanceserver{
server 127.0.0.1:8081;
server 127.0.0.1:8082;
}
# 将请求转发到负载均衡配置的服务器上
location / {
proxy_pass http://balanceserver;
index index.html index.htm;
}
我们直接通过轮询机制来访问首先向Session中存入一个姓名。
访问:http://localhost/createSession?name=yanggb
得到:【当前项目端口:8081】 【当前sessionId :D5312CBE049C0F486315CF550BFB255C】
因为我们使用的是默认的轮询策略,因为这次访问的是8081端口,那么下次访问的肯定是8082端口,我们可以直接获取到刚才存入Session的值。
访问:http://localhost/getSession
得到:【当前项目端口:8082】 【当前sessionId :D85157E33965BE6D7BB1E1CC0E43208F】 【获取的姓名:null】
这个时候我们会发现,8082端口中并没有我们存入的值,并且sessionId也是与8081端口不同。先想一想,这个时候我们是8082端口的服务器,但是之前我们是在8081端口中存入了一个姓名,那么我们现在来看看访问8081端口是否能获取到之前存入的姓名yanggb。
访问:http://localhost/getSession
得到:【当前项目端口:8081】 【当前sessionId :C5E2061BB03CE8FFE3E9FBDA00CFA28C】 【获取的姓名:null】
显然,8081端口中也获取不到之前存入的姓名yanggb。如果仔细地观察的话,会发现连sessionId都不一样了。原因是因为,在第二次去访问负载均衡服务器的时候,访问的是8082端口的服务器,这个时候客户端在cookie中获取到的是第一次访问8081端口的服务器时响应返回的sessionId,拿这个sessionId去8082端口的服务器上找是找不到的,因此8082端口就重新创建了一个sessionId并将这个sessionI响应返回给客户端,客户端拿这个sessionId替换掉了之前的8081端口服务器响应返回的sessionId。这样,当第三次访问的是8081端口的服务器的时候,就拿了一个在8081端口的服务器上找不到的sessionId去请求,导致又创建一个新的sessionId。这样就陷入了反复循环的境地,两个服务器永远拿到的是对方生成的sessionId,拿不到自己生成的sessionId。
解决这两个服务之间Session共享问题的方案:Spring-Session
Spring提供了一个解决方案:Spring-Session用来解决两个服务之间Session共享的问题。
要使用Spring-Session,需要在pom.xml中添加相关依赖。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</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>
同时,需要修改application.properties配置文件(本地要开启redis服务)。
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
#spring.redis.password=
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=8
spring.redis.timeout=10000
然后再在代码中添加Session配置类。
/**
* 这个类用配置redis服务器的连接
* maxInactiveIntervalInSeconds为SpringSession的过期时间(单位:秒)
*/
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class SessionConfig {
// 冒号后的值为没有配置文件时,制动装载的默认值
@Value("${redis.hostname:localhost}")
private String hostName;
@Value("${redis.port:6379}")
private int port;
// @Value("${redis.password}")
// private String password; @Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration =
new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(hostName);
redisStandaloneConfiguration.setPort(port);
// redisStandaloneConfiguration.setDatabase(0);
// redisStandaloneConfiguration.setPassword(RedisPassword.of("123456"));
return new JedisConnectionFactory(redisStandaloneConfiguration);
} @Bean
public StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
初始化Session配置
/**
* 初始化Session配置
*/
public class RedisSessionInitializer extends AbstractHttpSessionApplicationInitializer {
public RedisSessionInitializer() {
super(RedisSessionConfig.class);
}
}
这个时候再重新跑一次上面的测试,会发现能够拿到相同的Session信息,也就是实现了Session的共享。
Spring-Sesion实现的原理
当Web服务器接收到请求后,请求会进入对应的Filter进行过滤,将原本需要由Web服务器创建会话的过程转交给Spring-Session进行创建。Spring-Session会将原本应该保存在Web服务器内存的Session存放到Redis中。然后Web服务器之间通过连接Redis来共享数据,达到Sesson共享的目的。
"你离开以后,我遇见过很多女孩,像你的眉,像你的眼,但都不是你。"
分布式中session共享的解决方案:spring-session的更多相关文章
- 集群间Session共享问题解决方案
两个基本概念的生命周期 session: 当新客户端发现一个HTTP请求时服务端会创建一个session.并分配一个sessionID作为服务端来客户端的识别,session对象会保存在服务端.此时s ...
- Session共享的解决方案
http://www.cnblogs.com/xinhaijulan/archive/2010/08/21/1805116.html Session共享的解决方案 1.客户端SessionID值唯一: ...
- .net,sessionState的Session共享问题解决方案
最近项目因为要负载均衡所以就使用了sessionState的Session共享,但是却发现多台服务器中有个别服务器的Session没有共享,于是就有了这篇文章,下面开始说说. 这个基本上就分两种情况: ...
- 负载均衡服务器session共享的解决方案
在ASP.NET的程序中要使用Session对象时,必须确保页面的@page指令中EnableSessionState属性是True或者Readonly,并且在web.config文件中正确的设置了S ...
- Apache和Tomcat 配置负载均衡(mod-proxy方式)-无session共享、无粘性session
转:https://blog.csdn.net/wangjunjun2008/article/details/38268483 mod-proxy方式实现负载均衡是利用了Apache 2.x版本自带的 ...
- 负载均衡服务器session共享的解决方案 (转载)
http://luanzhz.blog.163.com/blog/static/58023129201101811445262/ 在ASP.NET的程序中要使用Session对象时,必须确保页面的@p ...
- Spring Session + Redis实现分布式Session共享
发表于 2016-09-29 文章目录 1. Maven依赖 2. 配置Filter 3. Spring配置文件 4. 解决Redis云服务Unable to configure Redis to k ...
- 170222、使用Spring Session和Redis解决分布式Session跨域共享问题
使用Spring Session和Redis解决分布式Session跨域共享问题 原创 2017-02-27 徐刘根 Java后端技术 前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用 ...
- Spring Cloud分布式Session共享实践
通常情况下,Tomcat.Jetty等Servlet容器,会默认将Session保存在内存中.如果是单个服务器实例的应用,将Session保存在服务器内存中是一个非常好的方案.但是这种方案有一个缺点, ...
随机推荐
- node.js中this指向失效解决
问题:在外部单独使用类实例对象的方法,this没有指向该类实例对象 代码如下 class CQH { hello() { let name = this.name(); console.log(`He ...
- Jsp学习笔记(2)——页面导航、表单、EL表达式
页面导航 有两种跳转页面的方法.重定向和请求转发 两者区别: 请求转发(forward) 重定向(rerect) 请求服务次数 1 2 是否保留第一次请求request范围的属性 保留 不保留 地址栏 ...
- C#上手练习4(Break、CONITINUE语句)
C# 中的 continue 语句有点像 break 语句.但它不是强制终止,continue 会跳过当前循环中的代码,强制开始下一次循环. 对于 for 循环,continue 语句会导致执行条件测 ...
- 异常处理类-Throwable源码详解
package java.lang; import java.io.*; /** * * Throwable是所有Error和Exceptiong的父类 * 注意它有四个构造函数: * Throwab ...
- css布局技巧
CSS用户界面样式 鼠标样式currsor li{ cursor:pointer: } 设置或检索在对象上移动鼠标指针采用何种系统预定义的光标形状 属性值 描述 default 默认 pointer ...
- whistle手机调试工具使用简单教程
npm全局安装 npm install -g whistle 全局启动 w2 start 启动之后,输入127.0.0.1:8899 就可以访问到whistle调试界面了: 我们主要常用几个功能: 1 ...
- HTML学习 day03
表单 表单 表单:表单域:包含了处理表单数据所用的程序的URL以及数据提交到服务器的方法. 表单控件:(对象.元素):包含了文本框.密码框.隐藏.多行文本框(文本域).复选框.单选框.下拉 ...
- ECharts grid组件离容器的距离
ECharts grid组件离容器的距离 由 Carrie 创建, 最后一次修改 2017-09-04 grid.left | string, number [ default: '10%' ...
- linux内核 mtd分区
首先 内核配置需要打开MTD选项 Memory Technology Devices (MTD) ---> 如果是NOR Flash,需要选择Common Flash Interface (CF ...
- python基础-并发编程02
并发编程 子进程回收的两种方式 join()让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源 from multiprocessing import Process import ti ...