解决分布式系统中的session共享问题

1、session复制(效率低,不使用)

图片解释

在这里插入图片描述

2、客户端存储(不安全,不使用)

图片解释

在这里插入图片描述

3、hash一致性(网络变导致ip变,不使用)

图片解释

在这里插入图片描述

4、统一存储(好用易配置,使用)

1)图片解释

在这里插入图片描述
在这里插入图片描述

2)具体配置

①加入spring session整合redis的依赖

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

该依赖来自于:1. Updating Dependencies

如果没有加入redis的依赖请加入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <!--使用Jmeter进行压测的时候会出现:
        Redis exception; nested exception is io.lettuce.core.RedisException: io.netty.util.internal.OutOfDirectMemoryError
        说明内存超了,这是因为连接springboot中连接redis用的是lettuce-core,而它底层用netty通信,而netty不会及时关闭连接,
        所以就会出现内存溢出问题
        -->
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--2、和上面redis依赖连用,用来排除lettuce-core,然后使用jedis连接redis,
具体解释可以看:https://blog.csdn.net/SearchB/article/details/110470094-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

②在application.properties中配置如下代码:

spring.session.store-type=redis

要看解释就看:2. Spring Boot Configuration

你还可以配置超时时间等其他配置,具体配置方式还是看上面的2. Spring Boot Configuration,例如:

spring.session.timeout: 30m

当然如果你不配置的话,session的过期时间就是30分钟

当然你还需要配置redis的信息,可以根据3. Configuring the Redis Connection来配置

③在启动类上添加@EnableRedisHttpSession注解,来自于:Spring Java Configuration

④配置cookie名称和cookie的domain(即范围),以及配置使用json的序列化机制
首先说一下cookie名称和范围是什么,我用谷歌浏览器举个例子:
在这里插入图片描述
然后我们来说一下它们的作用,首先名称就是一个指示作用,一般用我们自己的

然后说范围有什么用?这涉及域名问题,domain默认的值就是当前最多级别的域名,例如美团北京站是https://bj.meituan.com,那么默认domain就是bj.meituan.com,当你登录了美团北京站之后,假设你的信息保存在服务端的session中,这个时候浏览器中对应服务端session信息的cookie令牌中domain就是bj.meituan.com,如果你把当前美团站点切换成了上海站(url:https://sh.meituan.com),那么你会发现cookie令牌消失了,因此当前站点的默认domain是sh.meituan.com,这是因为美团北京站和美团上海站的默认domain不一样,所以cookie会消失,所以为了避免这种情况的出现,所以我们需要在服务端设置domain,例如我们将domain设置成了meituan.com,这样无论是美团北京站还是美团上海站都可以使用这个domain,因此浏览器中对应服务端session信息的cookie令牌就不会消失了

然后我们还需要将默认jdk序列化机制变成json序列化机制,这样做有两个原因,其一是默认使用jdk序列化机制需要让实体类实现Serializable接口,比较麻烦;其二当我们使用redis客户端工具去查看redis的时候发现jdk序列化后的内容无法识别,基于以上两点,因此我们需要将序列化机制变成json序列化机制

如果要实现以上两点,我们可以创建一个SessionConfig配置类,当然这个类名是随便起的,相应的解释上面也有具体的地址,具体做法如下:

@Configuration
public class SessionConfig {

    /**
     * 以下配置来自:https://docs.spring.io/spring-session/docs/2.1.13.RELEASE/reference/html5/guides/java-custom-cookie.html#custom-cookie-spring-configuration
     * 配置cookie名称
     * 配置cookie存储的路径,相同的二级域名下都会有这个cookie
     * @return
     */
    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("GULISESSION");
        serializer.setDomainName("gulimall.com");
        return serializer;
    }

    /**
     * 序列化机制使用json,而不用默认的jdk序列化机制,来自:https://github.com/spring-projects/spring-session/blob/2.1.13.RELEASE/samples/boot/redis-json/src/main/java/sample/config/SessionConfig.java
     * @return
     */
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }
}

以上内容来自spring官方网站,我带大家看一下如何找到上面链接中的配置内容;

首先我们找到spring session文档信息,如下:
在这里插入图片描述
在这里插入图片描述
进入文档之后先看HttpSession with Redis Guide,这里面有spring session整合redis的注解、spring session在application.properties中的配置信息、redis的配置信息等,然后在看点击5.2 HttpSession with Redis,这个里面主要介绍springsession在启动类上的注解是什么,如下:
在这里插入图片描述
然后我们来看配置文件中配置的json序列化配置来自哪里,如下:
在这里插入图片描述
在这里插入图片描述
然后我们来看如何配置cookie的名称、范围等等信息,如下:
在这里插入图片描述
在这里插入图片描述

3)spring session的原理

先从启动类上的@EnableRedisHttpSession注解看,按着Ctrl点进去,你会发现:

在这里插入图片描述
我们进入RedisHttpSessionConfiguration类,首先看里面的第一个大方法:

@Bean
public RedisOperationsSessionRepository sessionRepository()

我们按着Ctrl点RedisOperationsSessionRepository,然后我们看一下该类的结构,如下:

在这里插入图片描述
可以看出里面全部都是在Redis中对Session进行增删改查的操作,我们回到RedisHttpSessionConfiguration类,可以发现该类继承了SpringHttpSessionConfiguration类,我们进入SpringHttpSessionConfiguration类,发现里面有一个过滤器方法:

@Bean
public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter

我们进入SessionRepositoryFilter类,在该类的构造方法中可以看到该类的构造器,如下:

public SessionRepositoryFilter(SessionRepository<S> sessionRepository) {
	if (sessionRepository == null) {
		throw new IllegalArgumentException("sessionRepository cannot be null");
	}
	this.sessionRepository = sessionRepository;
}

里面有一行代码是:this.sessionRepository = sessionRepository;,我们找到该属性:

private final SessionRepository<S> sessionRepository;

如果你进入SessionRepository接口,你可以发现它的实现类有这么几个:

在这里插入图片描述

其中RedisOperationsSessionRepository类就是上面提到的在redis中进行session增删改查的那个类,我们回到SessionRepositoryFilter类中的构造方法,根据上面的分析,其实this.sessionRepository = sessionRepository这行代码中的sessionRepository对象对应的类就是RedisOperationsSessionRepository类,然后SessionRepositoryFilter类中还有一个doFilterInternal方法,如下:

@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)

我们来分析一下该方法:

@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

	request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);

	SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
			request, response, this.servletContext);
	SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
			wrappedRequest, response);

	try {
		filterChain.doFilter(wrappedRequest, wrappedResponse);
	}
	finally {
		wrappedRequest.commitSession();
	}
}

先来看request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository),其中该常量是public static final String SESSION_REPOSITORY_ATTR = SessionRepository.class .getName(),这就是session库的名称,现在来看这个库使用的就是RedisOperationsSessionRepository类,上面我们已经说过了,这个类用于在redis中进行session增删改查,然后看filterChain.doFilter(wrappedRequest, wrappedResponse),过滤器放行的时候放行的不是我们普通的requestresponse,而是经过处理之后的wrappedRequestwrappedResponse,其中的一个处理就是对session的处理,之前我们使用的session库是将session储存到内容中,现在使用的是RedisOperationsSessionRepository类来在redis中增删改查session信息,我们使用session的时候是这样做的:

@GetMapping("/oauth2.0/weibo/success")
public String weiboLogin(@RequestParam("code") String code, HttpSession session){
	………………………………
	// 将用户信息存储在session中
    session.setAttribute(SessionConstant.LOGIN_USER, entityVo);
    ………………………………
}

其中HttpSession session相当于HttpSession session = request.getSession(),此时的HttpServletRequest request其实就是我们上面提到的SessionRepositoryRequestWrapper wrappedRequest,正是这个原因,我们才能把改变默认的session库,而是使用RedisOperationsSessionRepository类去操作redis对session进行增删改差

最后补充一点session自动续期:虽然我们设置了session在redis中的过期时间,但是只要我们在浏览器页面上和服务器进行交互,spring session就会给redis中存储的session自动续期,虽然不是直接续满,但是会添加点过期时间,这是很棒的操作,非常人性化,毕竟谁也不想用会网站就要登录一次,那太恶心了

4)单点登录

当我们在微博官网(https://www.sina.com.cn)登录之后,你会发现我们在新浪官网(https://www.sina.com.cn)也会自动登录,这里面使用的就是单点登录,使用spring session仅仅可以解决相同二级域名下的session共享问题,但是无法解决这种甚至顶级域名都不同的情况,这种情况就需要我们进行自己写代码处理了,在谷粒商城项目中老师提供了部分思路,可以帮助我们理解单点登录是如何实现的,视频地址是:https://www.bilibili.com/video/BV1np4y1C7Yf,可以看第233集235集,里面没有什么复杂的操作,主要就是在url地址后面加上资源地址参数、加上token参数、在ssoserver.com中的cookie中存储token、在隐藏域中放置资源地址参数、将用户信息通过ssoserver.com存储在redis中、从redis中取出用户信息、对是否带有token进行判断、对cookie中是否带有token进行判断、对session中是否有用户信息进行判断等,我也绘制了一个时序图,用的是Rational Rose点我下载,提取码是:3ba1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值