首页 > IT职业 > IT图书 > Tomcat集群后引发的...
简单理解为,在Tomcat集群下,各个Tomcat服务是分布式的。所以必须要解决业务逻辑碰到的各种分布式的问题。下
Spring Session官方文档写的非常好,例子也很多,小伙伴们仔细阅读即可,这里简单介绍一下几个关键类及代码
springSessionRepositoryFilter org.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/ ,如需转载,请注明出处,否则将追究法律责任。