文章目录
前言
提示:提供四种session同步方案参考
提示:以下是本篇文章正文内容,下面案例可供参考
一、解决方案
1、tomcat 的 session复制同步方案
tomcat 的 session复制同步方案
解决方式:每个节点都复制其他节点的session
优点:web-server(tomcat)不需要额外开发,只需搭建tomcat集群即可,修改配置文件
缺点:数据传输占用带宽,内存限制
tomcat 是全局session复制,集群内每个tomcat的session完全同步
在大规模应用的时候,用户过多,集群内tomcat数量过多,session的全局复制会导致集群性能下降,因此,tomcat的数量不能太多,而且依赖tomcat容器移植性不好(一般不采用)
详细内容:https://tomcat.apache.org/tomcat-8.0-doc/cluster-howto.html
2、cookie客户端存储
解决方式:session存储在客户端的cookie中
优点:
服务器不需存储session,用户保存自己的 session信息到cookie中。节省服务端资源
缺点:
1.每次http请求,携带用户在cookie中的完整信息,浪费网络带宽
2.session数据放在cookie中,存在泄漏、篡改、窃取等安全隐患
3.session数据放在cookie中,cookie有长度限制4K,不能保存大量信息
3、nginx ip_hash一致性
解决方式:固定的用户固定的请求一台服务器
优点:
只需要改nginx配置,不需要修改应用代码
负载均衡,只要hash属性的值分布是均匀的,多台web-server的负载是均衡的
可以支持web-server水平扩展(session同步法是不行的,受内存限制)
缺点:
服务器重启可能导致部分session丢失,影响业务,如部分用户需要重新登录
如果服务器水平扩展,rehash后session重新分布,会有用户路由不到正确的session
但是以上缺点问题也不是很大,因为session本来都是有有效期的。所以这两种反向代理的方式可以使用
4、统一存储
解决方式:后端统一存储session
优点:
没有安全隐患;
可以水平扩展,数据库/缓存 水平切分即可;
服务器重启或者扩容都不会有session丢失。
缺点:
增加了一次网络调用;
如将所有的getSession方法替换为从Redis查数据的方式。
缺点可以用SpringSession完美解决。
二、实战方案
1、Spring Session方式(redis方式)
1.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--spring session session共享性-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
2.yaml
server:
servlet:
session:
timeout: 1800 # Session timeout. If a duration suffix is not specified, seconds is used.过期时间
spring:
......
redis:
host: 192.168.253.16
port: 6379
database: 0
# password: 123456 #默认为空
timeout: 3000ms #最大等待时间,超时则抛出异常,否则请求一直等待
lettuce:
pool:
max-active: 20 #最大连接数,负值表示没有限制,默认8
max-wait: -1 #最大阻塞等待时间,负值表示没限制,默认-1
max-idle: 8 #最大空闲连接,默认8
min-idle: 0 #最小空闲连接,默认0
session:
store-type: redis
redis:
flush-mode: on_save # Sessions flush mode.刷新模式
namespace: spring:session # Namespace for keys used to store sessions.前缀
3.application 开启
@EnableRedisHttpSession
4.使用HttpSession session.setAttribute();
!!!!!attribute 需要序列化
若是微服务,服务间引用的类 需要类路径一致
使用session的微服务也需要配置
5.默认jdk序列化方式,改为json序列化方式
6,子域作用域问题
不同服务(子域session问题)
aaa.com auth.aaa.com bbs.aaa.com
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
/**
* session 子域共享 + redis.json序列化方式
* @author: yang
* @time: 2021/11/4 8:39
*/
@Configuration
public class MySessionConfig {
/**
* cookie设置 子域共享
* @return
*/
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
//cookie名字
defaultCookieSerializer.setCookieName("xxxSESSION");
//域,设置为父级域名
defaultCookieSerializer.setDomainName("xxx.com");
//存储路径设为根路径,同一域名下多个项目共享该cookie
// defaultCookieSerializer.setCookiePath("/");
return defaultCookieSerializer;
}
/**
* redis 使用json序列化
* @return
*/
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
!!!Springsession实现原理: 装饰模式
1,@EnableRedisHttpSession注解中 导入配置类@Import({RedisHttpSessionConfiguration.class})
2,RedisHttpSessionConfiguration
RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration implement....{
......
//注入 使用redis操作session的封装类(增删改查) [SessionRepository]
@Bean RedisIndexedSessionRepository sessionRepository(){...}
......
}
3,SpringHttpSessionConfiguration 中获取上一步注入的session封装类
@Configuration(proxyBeanMethods = false)
public class SpringHttpSessionConfiguration implements ApplicationContextAware {
... ...
@PostConstruct
//初始化,设置Cookie序列化CookieSerializer
public void init() {
CookieSerializer cookieSerializer = (this.cookieSerializer != null) ? this.cookieSerializer
: createDefaultCookieSerializer();
this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
}
... ...
@Bean
//创建时,获取之前的 [SessionRepository]
public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(
SessionRepository<S> sessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);
sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
return sessionRepositoryFilter;
}
......
}
4,SessionResponsitory中装饰了request和response
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
//装饰模式包装增强 request,response
//wrappedRequest.getSession() 自定义实现
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
response);
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
finally {
wrappedRequest.commitSession();
}
}
5.wrappedRequest.commitSession()方法中
//从[SessionRepository] 获取session,即从redis获取session
S session = wrappedSession.getSession();
2、tomcat+redis cluster解决方案(老项目没使用spring框架,可以使用这种方式…)
详细阅读下链 作者readme文件
https://gitcode.net/mirrors/ran-jit/tomcat-cluster-redis-session-manager
1,jar包 放入tomcat/lib
commons-logging-1.2.jar
commons-pool2-2.4.2.jar
jedis-2.9.0.jar
tomcat-cluster-redis-session-manager-2.0.1.jar
2,配置系统环境变量 key--value
catalina.base TOMCAT_LOCATION
3,redis配置文件放入tomcat/conf
redis-data-cache.properties
4,修改tomcat/conf/content.xml
<Valve className="tomcat.request.session.redis.SessionHandlerValve" />
<Manager className="tomcat.request.session.redis.SessionManager" />
5,复制tomcat和项目,启动;配置nginx负载均衡.....
6,jsp测试取出session
<body>
<%
session.setAttribute("8050",123);
%>
sessionid:<%=session.getId()%><br>
redisexists:<%=JedisUtil.exists(session.getId())%><br>
redisexists:<%=JedisUtil.getObjectBykeyStr(session.getId())%><br>
sessionValue:<%=session.getAttribute("8050")%><br>
sessionip:<%=request.getServerName()%><br>
sessionPort:<%=request.getServerPort()%><br>
</body>