基于Tomcat单应用多模块隔离方案
2024-09-10 本文已影响0人
数齐
背景
目前我们Tomcat中包含的核心应用是acm,它由应用部署、监控、自动化多个模块组成。当有一个模块发生问题,会影响到其他模块的正常运行,但是从用户角度来说,这三个模块是相对独立的,不应该相互影响,所以本方案用来针对不同的模块进行线程资源隔离。
根因分析
首先我们需要弄明白为什么会相互影响。下图是我们的请求所经过的组件。
image.png
当请求通过Acceptor线程接收后封装成任务的形式给到Poller进行注册,此处使用NIO的标准流程进行处理。当有数据到达服务端时,会创建SocketProcessor,并将socket和事件相关信息传递过去,随后在Tomcat线程池中进行SocketProcessor的相关逻辑。通过上图可以看的出来,后续的流程都是同步进行的,只有等到具体的Servlet处理完业务逻辑后才会返回。最大的问题就是一个应用中所有的请求共用Tomcat处理线程池,当系统某个模块把链接完全占用后,其他模块拿不到对用的线程进行处理,导致堵塞设置超时,从而影响到上层的业务。
可能的思路
- 在Tomcat线程池根据请求的不同地址前缀发送到不同的线程池中进行处理。首先我们请求的前缀的获取,需要在HTTP协议解析之后获得,也就是图中的Processor这个过程进行处理的,但是我们将请求放入线程池处理的逻辑是在他的前面,所以无法实现。除非破坏Tomcat原有架构,风险极高且不推荐。
- Servlet3的异步化处理。在接收到请求之后,Servlet线程可以将耗时的操作委派给另一个线程来完成,自己在不生成响应的情况下返回至容器,以便能处理另一个请求。此时当前请求的响应将被延后,在异步处理完成后时再对客户端进行响应(异步线程拥有 ServletRequest 和 ServletResponse 对象的引用)。开启异步请求处理之后,Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。异步处理的特性可以帮助应用节省容器中的线程,特别适合执行时间长而且用户需要得到响应结果的任务,这将大大减少服务器资源的占用,并且提高并发处理速度。我们可以利用这个Servlet的这个特性,来完成我们的多模块的隔离方案。
方案验证
SpringMvc实现了Servlet3异步化的方案,主要的改动点是开启异步化的功能,在启动类上打上@EnableAsync注解,其次是在写Controller时,它的返回值使用Callable包裹。详情请见下面配置