分布式环境中的Session问题

目录

 

学习地址:

常见会话解决方案:

粘性会话 Sticky Session

会话复制 Session Replication

集中会话 Centralized Session

 SpringSession

定制 HttpSession

基于Redis的HttpSession

基本配置

application.properties

demo


学习地址:

《极客时间-玩转spring全家桶》分布式环境中如何解决Session的问题

常见会话解决方案:

粘性会话 Sticky Session

通过 Load Balance 来实现的,可以让来自同一个用户的会话尽可能的落在同一台机器上面,将分布式的Session变成单机的Session。

缺点:如果服务器下线了原先落在这台服务器的请求就会被分配到别的机器上,原先的会话就会失效了

会话复制 Session Replication

对每一台服务器上的会话都进行一个复制,这样整个集群的所有服务器都有几乎相同的会话信息,不管用户的会话落在哪个服务器上都可以得到它的session。

缺点:因为要对每一台服务器上的会话都进行一个复制,当访问量巨大的时候,需要的成本就变得很大了,每台机器上的Session副本信息也有可能不是一样的。

集中会话 Centralized Session

 使用JDBC、Redis来集中存储会话信息,只要有相同的JSESSIONID,就可以把Session从集中会话中取出来,保证了不管是请求落在哪个服务器上都能取到自己想要的Session信息。

 SpringSession

支持的存储:Redis,MongoDB,JDBC。SpringSession与这些容器是没有关系的,可以简化集群中的Session管理。

定制 HttpSession

通过封装的 HttpServletRequest 返回定制的 HttpSession。核心类:SessionRepositoryRequestWrapper、SessionRepositoryFilter、DelegatingFilterProxy。

简单看一下SessionRepositoryFilter类

首先是对request、response做了一个封装

 然后看SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper怎样获取session的

    public SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper.HttpSessionWrapper getSession(boolean create) {
        //首先获取一下session
        SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper.HttpSessionWrapper currentSession = this.getCurrentSession();
        //首先看有没有session
        if (currentSession != null) {
            return currentSession;
        } else {
            S requestedSession = this.getRequestedSession();
            //如果有拿到requestedSession,进行下面的操作
            if (requestedSession != null) {
                if (this.getAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR) == null) {
                    requestedSession.setLastAccessedTime(Instant.now());
                    this.requestedSessionIdValid = true;
                    currentSession = new SessionRepositoryFilter.SessionRepositoryRequestWrapper.HttpSessionWrapper(requestedSession, this.getServletContext());
                    currentSession.markNotNew();
                    this.setCurrentSession(currentSession);
                    return currentSession;
                }
            } else {
                if (SessionRepositoryFilter.SESSION_LOGGER.isDebugEnabled()) {
                    SessionRepositoryFilter.SESSION_LOGGER.debug("No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
                }

                this.setAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR, "true");
            }

            if (!create) {
                return null;
            } else if (SessionRepositoryFilter.this.httpSessionIdResolver instanceof CookieHttpSessionIdResolver && this.response.isCommitted()) {
                throw new IllegalStateException("Cannot create a session after the response has been committed");
            } else {
                if (SessionRepositoryFilter.SESSION_LOGGER.isDebugEnabled()) {
                    SessionRepositoryFilter.SESSION_LOGGER.debug("A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for " + SessionRepositoryFilter.SESSION_LOGGER_NAME, new RuntimeException("For debugging purposes only (not an error)"));
                }
                //如果没有拿到session,使用sessionRepository创建
                S session = SessionRepositoryFilter.this.sessionRepository.createSession();
                session.setLastAccessedTime(Instant.now());
                //设置在currentSession中
                currentSession = new SessionRepositoryFilter.SessionRepositoryRequestWrapper.HttpSessionWrapper(session, this.getServletContext());
                this.setCurrentSession(currentSession);
                return currentSession;
            }
        }
    }

基于Redis的HttpSession

基本配置

  • @EnableRedisHttpSession
  • 提供 RedisConnectionFactory
  • 实现 AbstractHttpSessionApplicationlnitializer
  • 配置 DelegatingFilterProxy

application.properties

//容器设置为Redis

spring.session.store-type=redis

//超时时间
spring.session.timeout=
serverservlet.session.timeout=
spring.session.redis.flush-mode=on-save
spring.session.redis.namespace=spring:session

demo

application.properties:

spring.redis.host=localhost

 引入依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-core</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

简单test 

@SpringBootApplication
@RestController
//开启支持
@EnableRedisHttpSession
public class SessionDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(SessionDemoApplication.class, args);
	}

	@RequestMapping("/hello")
	public String printSession(HttpSession session, String name) {
		String storedName = (String) session.getAttribute("name");
		if (storedName == null) {
			session.setAttribute("name", name);
			storedName = name;
		}
		return "hello " + storedName;
	}

}

Redis是空的

  

运行后Redis结果:

 因为session是存在了Redis中,只要是相同的JSESSIONID返回还是一样的,所以传参(name=s)或者重启服务,还是会返回(hello high)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值