分布式中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保存在服务器内存中是一个非常好的方案.但是这种方案有一个缺点, ...
随机推荐
- createscope
/// <summary> /// Creates a new <see cref="IServiceScope"/> that can be used t ...
- Elasticsearch 6.x版本全文检索学习之分布式特性介绍
1.Elasticsearch 6.x版本全文检索学习之分布式特性介绍. 1).Elasticsearch支持集群默认,是一个分布式系统,其好处主要有两个. a.增大系统容量,如内存.磁盘.使得es集 ...
- C# consume RestApi
1.RestSharp. Nuget install RestSharp,Newtonsoft.Json. using System; using RestSharp; using Newtonsof ...
- Razor_02 第一个应用程序+Model+EF 添加
第一个应用程序+Model+EF 添加 小试牛刀 今天 也试了试 边说边写,但是 有时候 编辑器不给力,或者网路不给力,倒是浪费大家时间,所以今天录制完了就裁切了 部分视频,如果有不清楚的地方,可以留 ...
- CAD打印图纸要怎么操作?简单方法分享给你
大家日常生活中多多少少的都接触到过CAD文件,CAD图是借助CAD制图软件来进行绘制完成的.唯一的困惑就是CAD图纸的格式大多数均为dwg格式的,查看起来不是那么的方便?所以很多设计师们都会选择将图纸 ...
- GO基础之变量的使用
Go语言:是静态类型语言,因此变量(variable)是有明确类型的,编译器也会检查变量类型的正确性. 一.基本类型 变量的声明:全局变量必须有关键字var var name [type] 指定数据 ...
- XML的互相序列化对象
using System.Xml.Serialization; using System.IO; using System.Xml; namespace Common { public class X ...
- javafx笔记----非javafx线程Platform.runLater赋值不生效情况
Platform.runLater(() -> { // }); Platform.runLater一些情况下没有赋值到fx页面上 采用task方式 Task<SB> task = ...
- HTTP协议解析之Cookie
" Cookie与身份认证." 提到HTTP协议,不可避免地都会牵涉到Cookie,可以说,Cookie作为HTTP的重要组成部分,促进了HTTP协议的发展壮大. HTTP协议如果 ...
- 将vue2.9.6升级到vue3.0
vue2.9.6没有ui界面可以用,直接使用yarn global add @cli没用,还是2.9.6版本 借鉴博客: https://juejin.im/post/5bf7d67c51882518 ...