SpringCloud 学习-SpringSession介绍,使用,整合项目解决Session常规问题
1. SpringCloud为什么要用SpringSession? HttpSession不行吗?
HttpSession的问题:
- 不同服务,session不能共享的问题:不能跨不同域名之间共享session的数据,因为不同域名之间,cookie中的sessionID是不共享的.
- 同一服务,复制在多台服务器,session不同步的问题:session的数据存在服务器中,而分布式微服务环境下,同一服务模块可能存在多台服务器,服务器之间的session也无法共享
1.1 session 不同步问题的解决方法
方法一:session复制 (适合小型系统)
在同一个服务的不同服务器之间,同步session的数据.
- 优点:
- web-server(Tomcat) 原生支持,只需要修改配置文件
- 缺点:
- session同步需要数据传输,占用大量网络带宽,降低了服务器群的业务处理能力
- 任意一台web-server保存的数据都是所有web-server的session总和,数据冗余
- 大量分布式的集群情况下,由于所有的web-server都全量保存数据,所以此方法可取.
方法二:利用ip_hash一致性 (可使用)
在nginx负载均衡时,利用ip地址的hash一致性,所有该用户的请求都控制的发往固定的服务器.
- 优点:
- 只需要修改nginx的配置,不需要修改应用代码
- 负载均衡,只要hash属性的值分布式均衡的,多台web-server的负载也是均衡的
- 可以支持web-server的水平拓展(不受内存限制,区别于session复制方法)
- 缺点:
- session还是存在web-server中,所有web-server重启可能导致部分session丢失,影响业务
方法三:统一存储(分布式微服务情况下推荐使用)
无论哪一个web-server,session都不存在服务器中,而是统一存储在数据库或者redis类缓存中.
- 优点:
- 没有安全隐患
- 可以水平扩展
- session不会丢失
- 缺点:
- 增加了一次网络调用.
- 需要修改应用代码,如果将所有的getSession方法替换为从redis查数据的方式,redis获取数据会比内存中慢很多
注意:以上的缺点,可以使用SpringSession完美解决
1.2 session不能跨域共享的问题
解决方法:子域之间共享JSESSIONID
在存储session时,会将JSESSIONID保存在浏览器,并指定域名范围,因此在指定域名范围时,使用父域名,则该JSESSIONID在所有父子域名中生效.
注意:可以使用SrpingSession完成修改有效域名范围
2. SpringBoot 整合SpringSession
导入依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-bom</artifactId>
<version>Corn-SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
</dependencies>
修改配置文件
server:
port: 20000
servlet:
session:
timeout: 30m #session过期时间 默认30m
spring:
redis: #配置redis连接信息
host: 192.168.231.1
port: 6379
session:
store-type: redis #指定使用redis 存储session
添加@EnableRedisHttpSession 开启SpringSession
@EnableRedisHttpSession
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
将需要存放在session中的对象添加序列化接口Serializable
SpringSession需要将存入session的对象序列化后存放在redis中,默认采用字节码序列化,可添加配置类修改为JSON形式
public class Member implements Serializable{
private Long id;
}
SpringSession的配置
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
@Configuration
public class GulimailSessionConfig {
//配置JSESSIONID的作用域
@Bean
public CookieSerializer cookieSerializer(){
DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
defaultCookieSerializer.setCookieName("GULIMAILSESSION");
defaultCookieSerializer.setDomainName(".gulimail.com"); //将JSESSIONID作用域提高到父域名
return defaultCookieSerializer;
}
//配置序列化类型为JSON
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
return new GenericFastJsonRedisSerializer();
}
}
使用session保存数据
使用方法域普通session相同:
session.setAttribute("loginUser",member);
添加完成后,redis会创建出缓存数据和过期时间
使用session取数据:
<a href="http://auth.gulimail.com/login.html">你好,请登录:[[${session.loginUser.nickname}]]</a>
- 若跨服务模块进行session使用,需要在取数据的模块的同一路径下存放相同的实体类Member,否则会报序列化找不到类的错误.