ITPub博客

首页 > IT职业 > IT图书 > Tomcat集群后引发的...

Tomcat集群后引发的...

IT图书 作者:danielliu 时间:2021-09-09 09:07:17 0 删除 编辑
https://www.imooc.com/article/17545 《大型项目架构演进过程及思考的点》 这篇手记,里面写的非常之详细。
一期项目-Java从零到企业级电商项目实战 碰到的各种问题

简单理解为,在Tomcat集群下,各个Tomcat服务是分布式的。所以必须要解决业务逻辑碰到的各种分布式的问题。下

https://docs.spring.io/spring-session/docs/current/reference/html5/

Spring Session官方文档写的非常好,例子也很多,小伙伴们仔细阅读即可,这里简单介绍一下几个关键类及代码

DelegatingFilterProxy

        springSessionRepositoryFilterorg.springframework.web.filter.DelegatingFilterProxy

通过配置这个filter来做代理,它获取了委托的过滤器。通过源码结构可知图片描述

它继承了GenericFilterBean其中GenericFilterBean的init方法调用了DelegatingFilterProxy的initFilterBean(),那其实这里面就是获取委托的过滤器啦。并调用委托过滤器的doFilter()。

源码片段

    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
    }

然后注意看init的实现类都有哪些?图片描述观察蓝色背景OncePerRequestFilter这个类,名字起的很好。见名知意,继续看。

图片描述

如图,而主角SessionRepositoryFilter正是继承了这个抽象类。通过Attribute判断是否已经被过滤。而这个Attribute的key就是

SessionRepository.class.getName();

先跳出来继续说。

另外两个主角就是

SessionRepositoryRequestWrapper

和

SessionRepositoryResponseWrapper

例如SessionRepositoryRequestWrapper继承了HttpServletWrapper,所以它可以在目前的HttpRequest上进行包装,重写了getSession方法。

那Spring Session是如何和Redis如何交互的呢,session的有效期如何配置呢?那就要看看

org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration

还有

org.springframework.session.data.redis.RedisOperationsSessionRepository

这两个类啦,例如下面的代码是描述spring session存储在redis的session的namespace的一个常量


    /**
     * The default prefix for each key and channel in Redis used by Spring Session.
     */
    static final String DEFAULT_SPRING_SESSION_REDIS_PREFIX = "spring:session:";

还有下面这段,通过Spring Schedule来清理过期session

    @Scheduled(cron = "0 * * * * *")
    public void cleanupExpiredSessions() {
        this.expirationPolicy.cleanExpiredSessions();
    }

而Cookie的注入等默认设置(当然自己修改也是ok的,我们的项目就会通过注入的方式修改它,例如domain、path等等)

org.springframework.session.web.http.DefaultCookieSerializer

这个类有很多写的有意思的地方,例如servlet3才开始支持httponly,源码是这么判断的

/**
     * Returns true if the Servlet 3 APIs are detected.
     *
     * @return whether the Servlet 3 APIs are detected
     */
    private boolean isServlet3() {
        try {
            ServletRequest.class.getMethod("startAsync");
            return true;
        }
        catch (NoSuchMethodException e) {
        }
        return false;
    }

那其实spring session框架是真的非常有意思,里面牵扯的点非常多,redis、cookie、session,wrapper,proxy等等,那这些呢详细的在 Java企业级电商项目架构演进之路Tomcat集群和Redis分布式课程 中也会详细来讲解的。

我们先跳出来,以上两种方式都行,也各有优缺点,第一种更灵活,第二种对业务的侵入性更低。

解决定时任务分布式调度问题

问题的原因是我们有定时关单的Job,单个Tomcat OK,没有任何问题,但是在集群环境下,Spring Schedule定时执行的时候,会都一起执行,然而我们只希望执行一个就可以啦,避免数据错乱和资源浪费。所以在集群环境下,这种case也必须解决。

那分布式锁如果搞不好非常容易造成死锁,所以这种场景下要格外细致。

以Redisson实现分布式锁为例。

    @Scheduled(cron="0 */1 * * * ?")//每1分钟(每个1分钟的整数倍)
    public void closeOrderTaskV4() throws InterruptedException {
        RLock lock = redissonManager.getRedisson().getLock(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
        boolean getLock = false;
        try {
            if(getLock = lock.tryLock(2,50, TimeUnit.SECONDS)){//trylock增加锁
                log.info("===获取{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
                int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
                iOrderService.closeOrder(hour);
            }else{
                log.info("===没有获得分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            }
        }finally {
            if(!getLock){
                return;
            }
            log.info("===释放分布式锁:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            lock.unlock();
        }
    }

当然这个方法名字是closeOrderTaskV4,就代表着我们还有closeOrderTaskV1、closeOrderTaskV2、closeOrderTaskV3。架构演进,代码也是要演进的,从复杂到简单拆解,从不完美到完美这么一个过程,从原生实现到框架解析,代码不断升级优化,并对比其中业务场景,缺陷,优点这个过程还是非常有意思的。

解决本地guava cache迁移问题

问题的原因是Tomcat之前使用的guava cache,它只存在于tomcat实例上,tomcat及tomcat之间并不共享,所以必须迁移。否则负载均衡就TomcatA存储了guava cache,TomcatB想拿并拿不到...就尴尬了。

继续解决...

那在Tomcat集群环境下根据实际的业务场景,会有很多地方深入到代码的演进,以上3个点仅仅是一小部分。从项目架构到系统架构的思维转变,然后通过系统架构再深入到项目架构,再深入到代码当中,这个过程还是非常有趣味的。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/866/viewspace-2799714/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-08-19

  • 博文量
    119
  • 访问量
    810