Session共享方式

Session共享方式

标签(空格分隔): javaweb session sso


服务器之间共享

两个服务器通过同步session实现session共享
缺点:如果机器多了,就会出现大量的网络传输,甚至容易引起网络风暴,导致系统崩溃,只能适合少数的机器。

Spring-Session

实现原理

就是当Web服务器接收到http请求后,当请求进入对应的Filter进行过滤,将原本需要由web服务器创建会话的过程转交给Spring-Session进行创建,本来创建的会话保存在Web服务器内存中,通过Spring-Session创建的会话信息可以保存第三方的服务中,如:redis,mysql等。Web服务器之间通过连接第三方服务来共享数据,实现Session共享!

部分源码解析

SessionRepositoryFilter和JedisConnectionFactory注册过程

流程:

1、启动WEB项目的时候,会读取web.xml,读取顺序content-param --> listener --> filter --> servlet

2、ContextLoaderListener监听器的作用就是启动Web容器时,自动装配ApplicationContext的配置信息

3、初始化web应用程序上下文。

4、SpringHttpSessionConfiguration注册 springSessionRepositoryFilter :bean,RedisHttpSessionConfiguration 注册 sessionRedisTemplate : bean  和 sessionRepository : bean

5、配置文件配置JedisConnectionFactory implements RedisConnectionFactory ,创建 jedisConnectionFactory bean

RedisHttpSessionConfiguration继承了SpringHttpSessionConfiguration。

public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
        implements EmbeddedValueResolverAware, ImportAware {

SpringHttpSessionConfiguration 创建一个名称为springSessionRepositoryFilter的bean

@Bean
    public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(
            SessionRepository<S> sessionRepository) {
        SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(sessionRepository);
        sessionRepositoryFilter.setServletContext(this.servletContext);
        if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) {
            sessionRepositoryFilter.setHttpSessionStrategy(
                    (MultiHttpSessionStrategy) this.httpSessionStrategy);
        }
        else {
            sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);
        }
        return sessionRepositoryFilter;
    }

创建RedisHttpSessionConfiguration#RedisTemplate bean的名称为sessionRedisTemplate

    @Bean
    public RedisTemplate<Object, Object> sessionRedisTemplate(
            RedisConnectionFactory connectionFactory) {
        //实例化 RedisTemplate 
        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
        //设置key序列化 StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        //设置Hash key  StringRedisSerializer
        template.setHashKeySerializer(new StringRedisSerializer());
        if (this.defaultRedisSerializer != null) {
            template.setDefaultSerializer(this.defaultRedisSerializer);
        }
        //设置 connectionFactory。第五步创建的(实际connectionFactory加载过程和讲解过程顺序不一样)
        template.setConnectionFactory(connectionFactory);
        return template;
    }

创建RedisHttpSessionConfiguration#RedisOperationsSessionRepository bean的名称为sessionRepository

    @Bean
    public RedisOperationsSessionRepository sessionRepository(
    //使用sessionRedisTemplate bean
        @Qualifier("sessionRedisTemplate") 
        RedisOperations<Object, Object> sessionRedisTemplate,
        ApplicationEventPublisher applicationEventPublisher) {
            
            //實例化RedisOperationsSessionRepository
        RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
                sessionRedisTemplate);
                //設置applicationEventPublisher
        sessionRepository.setApplicationEventPublisher(applicationEventPublisher);
        //設置最大的失效時間 maxInactiveIntervalInSeconds = 1800
        sessionRepository
                .setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
        if (this.defaultRedisSerializer != null) {
            sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
        }

        String redisNamespace = getRedisNamespace();
        if (StringUtils.hasText(redisNamespace)) {
            sessionRepository.setRedisKeyNamespace(redisNamespace);
        }

        sessionRepository.setRedisFlushMode(this.redisFlushMode);
        return sessionRepository;
    }
SessionRepositoryFilter添加到FilterChain

流程:

1.在Servlet3.0规范中,Servlet容器启动时会自动扫描javax.servlet.ServletContainerInitializer的实现类,在实现类中我们可以定制需要加载的类。 通过注解@HandlesTypes(WebApplicationInitializer.class),让Servlet容器在启动该类时,会自动寻找所有的WebApplicationInitializer实现类。
2.insertSessionRepositoryFilter 方法通过filterName获取 SessionRepositoryFilter ,并创建了 new DelegatingFilterProxy(filterName);
3.然后将filter添加到FilterChain中

ServletContainerInitializer的实现类加载和通过注解@HandlesTypes(WebApplicationInitializer.class)实现类的加载

//加载实现类
@HandlesTypes(WebApplicationInitializer.class)
//SpringServletContainerInitializer实现ServletContainerInitializer
public class SpringServletContainerInitializer implements ServletContainerInitializer {
}

AbstractHttpSessionApplicationInitializer实现WebApplicationInitializer进行加载

@Order(100)
public abstract class AbstractHttpSessionApplicationInitializer
        implements WebApplicationInitializer {
        }

AbstractHttpSessionApplicationInitializer#onStartup

public void onStartup(ServletContext servletContext) throws ServletException {
    beforeSessionRepositoryFilter(servletContext);
    if (this.configurationClasses != null) {
        AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
        rootAppContext.register(this.configurationClasses);
        servletContext.addListener(new ContextLoaderListener(rootAppContext));
    }
    //添加Filter
    insertSessionRepositoryFilter(servletContext);
    afterSessionRepositoryFilter(servletContext);
    }

AbstractHttpSessionApplicationInitializer#insertSessionRepositoryFilter

/**
 * 注册springSessionRepositoryFilter
 * @param servletContext the {@link ServletContext}
 */
private void insertSessionRepositoryFilter(ServletContext servletContext) {
    // DEFAULT_FILTER_NAME = "springSessionRepositoryFilter"
    String filterName = DEFAULT_FILTER_NAME;
    //通过filterName创建 DelegatingFilterProxy
    DelegatingFilterProxy springSessionRepositoryFilter = new DelegatingFilterProxy(
            filterName);
    String contextAttribute = getWebApplicationContextAttribute();
    if (contextAttribute != null) {
        springSessionRepositoryFilter.setContextAttribute(contextAttribute);
    }
    //根据filterName和上下文添加Filter到FilterChain
    registerFilter(servletContext, true, filterName, springSessionRepositoryFilter);
}

AbstractHttpSessionApplicationInitializer#registerFilter

private void registerFilter(ServletContext servletContext,
            boolean insertBeforeOtherFilters, String filterName, Filter filter) {
    Dynamic registration = servletContext.addFilter(filterName, filter);
    if (registration == null) {
        throw new IllegalStateException(
                "Duplicate Filter registration for '" + filterName
                        + "'. Check to ensure the Filter is only configured once.");
    }
    //是否支持异步,默认 true
    registration.setAsyncSupported(isAsyncSessionSupported());
    //得到DispatcherType springSessionRepositoryFilter
    EnumSet<DispatcherType> dispatcherTypes = getSessionDispatcherTypes();
    //添加一个带有给定url模式的筛选器映射和由这个FilterRegistration表示的过滤器的分派器类型。 过滤器映射按照添加它们的顺序进行匹配。
    registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters,
            "/*");
}

addFilter将Filter添加到ServletContext中

public FilterRegistration.Dynamic addFilter(String filterName, Filter filter);
SessionRepositoryFilter拦截过程
1、请求被DelegatingFilterProxy拦截到,然后执行doFilter方法,在doFilter中找到执行的代理类。
2、OncePerRequestFilter代理Filter执行doFilter方法,然后调用抽象方法doFilterInternal
3、SessionRepositoryFilter继承了OncePerRequestFilter,实现了doFilterInternal,这个方法一个封装一个wrappedRequest,通过执行commitSession保存session信息到redis

请求进来,被DelegatingFilterProxy 拦截到,在web.xml中进行了配置
执行filter方法

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    // 如果需要,延迟初始化委托。 necessary.
    Filter delegateToUse = this.delegate;
    if (delegateToUse == null) {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                WebApplicationContext wac = findWebApplicationContext();
                if (wac == null) {
                    throw new IllegalStateException("No WebApplicationContext found: " +
                            "no ContextLoaderListener or DispatcherServlet registered?");
                }
                this.delegate = initDelegate(wac);
            }
            delegateToUse = this.delegate;
        }
    }

    // 让委托执行实际的doFilter操作
    invokeDelegate(delegateToUse, request, response, filterChain);
}

initDelegate

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
//可以获取到SessionRepositoryFilter [备注1]
        Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
    }

//[备注1] 因为 :SessionRepositoryFilter是一个优先级最高的javax.servlet.Filter
/*
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilter<S extends ExpiringSession>
        extends OncePerRequestFilter {

*/

delegate.doFilter();

protected void invokeDelegate(
            Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //代理去执行doFilter,代理为SessionRepositoryFilter
        delegate.doFilter(request, response, filterChain);
    }

OncePerRequestFilter#doFilter

public final void doFilter(ServletRequest request, ServletResponse response,
        FilterChain filterChain) throws ServletException, IOException {

    if (!(request instanceof HttpServletRequest)
            || !(response instanceof HttpServletResponse)) {
        throw new ServletException(
                "OncePerRequestFilter just supports HTTP requests");
    }
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    boolean hasAlreadyFilteredAttribute = request
            .getAttribute(this.alreadyFilteredAttributeName) != null;

    if (hasAlreadyFilteredAttribute) {

        //在不调用此过滤器的情况下进行…
        filterChain.doFilter(request, response);
    }
    else {
        // 调用这个过滤器…
        request.setAttribute(this.alreadyFilteredAttributeName, Boolean.TRUE);
        try {
        //doFilterInternal是个抽象方法
            doFilterInternal(httpRequest, httpResponse, filterChain);
        }
        finally {
            // 删除此请求的“已过滤”请求属性。
            request.removeAttribute(this.alreadyFilteredAttributeName);
        }
    }
}

执行SessionRepositoryFilter#doFilterInternal

@Override
protected void doFilterInternal(HttpServletRequest request,
        HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
    //使用HttpServletRequest 、HttpServletResponse和servletContext创建一个SessionRepositoryRequestWrapper

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

    //使用CookieHttpSessionStrategy重新包装了 HttpServletRequest
    HttpServletRequest strategyRequest = this.httpSessionStrategy
            .wrapRequest(wrappedRequest, wrappedResponse);
    HttpServletResponse strategyResponse = this.httpSessionStrategy
            .wrapResponse(wrappedRequest, wrappedResponse);

    try {
    //执行其他过滤器
        filterChain.doFilter(strategyRequest, strategyResponse);
    }
    finally {
    //保存session信息
        wrappedRequest.commitSession();
    }
}
SessionRepository保存session数据
1、提交session保存
2、获取当前session,这一步比较重要,获取了一个HttpSessionWrapper,这个HttpSessionWrapper替换了HTTPSession
3、wrappedSession获取当前的Session
4、使用 RedisTemplate 保存Session内容,并通过调用RedisConnection 使用它的实现类JedisClusterConnection获取redis连接

SessionRepository#commitSession

/**
*使用HttpSessionStrategy将会话id写入响应。 *保存会话。
*/
private void commitSession() {
    HttpSessionWrapper wrappedSession = getCurrentSession();
    if (wrappedSession == null) {
        if (isInvalidateClientSession()) {
            SessionRepositoryFilter.this.httpSessionStrategy
                    .onInvalidateSession(this, this.response);
        }
    }
    else {
        S session = wrappedSession.getSession();
        SessionRepositoryFilter.this.sessionRepository.save(session);
        if (!isRequestedSessionIdValid()
                || !session.getId().equals(getRequestedSessionId())) {
            SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session,
                    this, this.response);
        }
    }
}

SessionRepository#getCurrentSession

//会话存储库请求属性名。
public static final String SESSION_REPOSITORY_ATTR = SessionRepository.class
            .getName();
            
private static final String CURRENT_SESSION_ATTR = SESSION_REPOSITORY_ATTR
            + ".CURRENT_SESSION";

private HttpSessionWrapper getCurrentSession() {
    return (HttpSessionWrapper)
    //获取session
    getAttribute(CURRENT_SESSION_ATTR);
}
        
   /**
     * 此方法的默认行为是在包装请求对象上调用getAttribute(字符串名称)。
     */
    public Object getAttribute(String name) {
    //这里的request就是上面封装的
        return this.request.getAttribute(name);
    }

wrappedSession.getSession

//返回 RedisSession
S session = wrappedSession.getSession();
//-------------------------
public S getSession() {
        return this.session;
    }
class ExpiringSessionHttpSession<S extends ExpiringSession> implements HttpSession {
    private S session;
final class RedisSession implements ExpiringSession {

save,实际是调用 RedisOperationsSessionRepository的 RedisOperations 操作

SessionRepositoryFilter.this.sessionRepository.save(session);

//this.sessionRepository =  SessionRepository<S> sessionRepository;

//--------------------------------
//这个RedisOperationsSessionRepository是之前就创建好的
public class RedisOperationsSessionRepository implements
        FindByIndexNameSessionRepository<RedisOperationsSessionRepository.RedisSession>,
        MessageListener {
        
        
public interface FindByIndexNameSessionRepository<S extends Session>
        extends SessionRepository<S> {
        
//---------------------------


    public void save(RedisSession session) {
        //4.1saveDelta
        session.saveDelta();
        if (session.isNew()) {
            //4.2调用
            String sessionCreatedKey = getSessionCreatedChannel(session.getId());
            //4.3convertAndSend
            //RedisOperations = this.sessionRedisOperations
            this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
            session.setNew(false);
        }
    }
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值