分布式Session共享多种方案,SpringSession;tomcat+redis集群


前言

提示:提供四种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>

总结

有问题欢迎交流.

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值