Tomcat启动后再启动另一个线程执行别的任务与Servlet获取Spring中的参数

本文介绍了一个基于Servlet的简单定时任务处理机制。通过继承HttpServlet并重写doGet和doPost方法来响应HTTP请求,同时利用Java Timer进行周期性任务调度。
年后的第一篇,先上代码:

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//此为Servlet类
public class NucleonEventProcessor extends HttpServlet {
private static final long serialVersionUID=-9045451275234606838L;
int num=0;//保证线程启动一次后就不再启动


Timer timer=new Timer();

//以下两个为了从spring中获取注入的service值
ServletContext servletContext=null;
WebApplicationContext webApplicationContext=null;

//以下为某个Service
//PersonService personService=null;


/**
* Constructor of the object.
*/
public NucleonEventProcessor() {
super();
}

/**
* Destruction of the servlet. <br>
*/
public void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}

/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}

/**
* The doPost method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to post.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the POST method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}

/**
* Initialization of the servlet. <br>
*
* @throws ServletException if an error occurs
*/
public void init() throws ServletException {
// Put your code here
FutureTask<String> task=new FutureTask<String>(new Callable<String>() {

@Override
public String call() throws Exception {
// TODO Auto-generated method stub
start();
return "Collection Completed";
}
});
servletContext=getServletContext();
webApplicationContext=WebApplicationContextUtils.getWebApplicationContext(servletContext);
//获取spring配置文件中的service
//personService=(PersonService)webApplicationContext.getBean("personService");

//线程启动
new Thread(task).start();
}
private void start(){
//要执行的方法
if(num=0){
timeschedule();
num++;
}
}
public void timeschedule(){
timer.schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
//要执行的方法
start();
}
}, 1000,2000);//1秒后执行,每隔2秒执行一次
}
}


上面的代码是整个类的代码,大部分是继续HttpServlet后自动生成的,重要的是
【1】init【2】timeschedule里边的内容
<think>我们面对的是Tomcat 9.0.99启动时卡在“正在启动Servlet引擎”的问题。这个问题通常Web应用程序的初始化有关,可能由于Servlet、Filter或Listener的初始化过程耗时过长或出现死锁等原因导致。 解决思路: 1. 检查日志:查看Tomcat的日志文件(catalina.out或localhost.log),寻找是否有异常或错误信息。 2. 定位问题应用:如果是多个应用,尝试逐个部署以确定是哪个应用导致的问题。 3. 线程分析:在Tomcat卡住时,获取线程转储(thread dump),分析线程状态,看是否有死锁或长时间阻塞的线程。 4. 调整配置:增加Tomcat启动超时时间,或者调整应用中的初始化逻辑。 具体步骤: ### 1. 检查日志 查看Tomcat的日志文件,通常位于`logs`目录下,重点关注`catalina.out`和`localhost_<日期>.log`。如果应用有初始化错误,通常会在日志中体现。 ### 2. 定位问题应用 如果部署了多个应用,可以尝试将应用逐个移除,然后重启Tomcat,直到找到导致问题的应用。找到问题应用后,再具体检查该应用的初始化代码(如Servlet的init方法、Filter的init方法、ServletContextListener的contextInitialized方法等)。 ### 3. 获取线程转储 当Tomcat卡在“正在启动Servlet引擎”时,我们可以获取线程转储来分析线程状态。 在Linux系统下,可以使用以下命令获取Tomcat线程转储: ```bash # 先找到Tomcat的进程ID ps -ef | grep tomcat # 然后使用jstack获取线程转储 jstack -l <pid> > thread_dump.txt ``` 在Windows系统下,可以使用任务管理器找到Tomcat进程的PID,然后使用命令行: ```bash jstack -l <pid> > thread_dump.txt ``` 分析线程转储,查看是否有线程处于死锁状态(deadlock)或长时间等待(如WAITING、BLOCKED)。特别关注那些在初始化过程中被阻塞的线程。 ### 4. 调整配置 - **增加启动超时时间**:在Tomcat的`server.xml`中,可以设置`startStopThread`属性来增加启动超时时间。例如,在`<Host>`元素中添加`startStopThreads="2"`(表示使用两个线程并行启动应用)和`startStopThreadPriority="5"`(线程优先级)可能有助于提高启动速度,但更关键的是增加超时时间。实际上,Tomcat并没有直接提供启动超时配置,但可以通过在`Context`中设置`startupTimeout`来为单个Web应用设置启动超时(单位毫秒): ```xml <Context startupTimeout="600000"> <!-- 10分钟 --> ``` 将这个配置放在应用对应的`Context`配置中。 - **检查应用初始化**:在应用的初始化代码中,避免执行耗时操作(如大量数据的加载、网络请求等)。如果必须执行,考虑使用异步方式或放到应用启动执行。 - **内存设置**:如果是因为内存不足导致垃圾回收时间过长,可以尝试增加Tomcat的内存设置。在`catalina.sh`(Linux)或`catalina.bat`(Windows)中设置环境变量: ```bash export JAVA_OPTS="-Xms512m -Xmx1024m" ``` ### 5. 其他可能的原因 - **数据库连接池初始化问题**:如果应用启动时需要初始化数据库连接池,而数据库连接不上,可能会导致长时间等待。检查数据库连接配置,以及数据库是否可用。 - **网络问题**:如果应用在初始化过程中需要访问网络资源(如远程Web服务、文件共享等),网络延迟或不可达可能导致阻塞。 - **循环依赖**:在Spring等框架中,Bean之间的循环依赖可能导致初始化死锁。检查应用框架的日志,看是否有相关提示。 如果以上方法都不能解决问题,可以考虑升级Tomcat到最新版本(因为9.0.99可能是一个测试版本,存在已知问题),或者使用Tomcat 9.0.x的稳定版本。 最后,建议在开发环境中模拟生产环境配置,以便更好地定位问题。 相关问题: 1. 如何分析Tomcat线程转储? 2. 如何优化Tomcat启动速度? 3. 在Spring应用中,如何避免启动时死锁? 4. Tomcat的常见启动问题有哪些? 5. 如何配置Tomcat的内存参数?</think>针对 Tomcat 9.0.99 启动卡在"正在启动 Servlet 引擎"的问题,以下是系统性的解决方案: ### 1. **检查应用初始化代码** - 重点排查 `ServletContextListener` 的 `contextInitialized()` 方法 - 检查所有 `@WebServlet` 和 `@WebFilter` 的 `init()` 方法 - 常见问题:数据库连接阻塞、大文件加载、同步锁死等 ```java // 示例:有问题的初始化代码 public void contextInitialized(ServletContextEvent sce) { while(true) { // 死循环导致卡死 // 初始化操作 } } ``` ### 2. **分析线程转储(关键步骤)** 1. 当 Tomcat 卡住时执行: ```bash # Linux/Mac jstack <tomcat_pid> > thread_dump.txt # Windows jstack -l <tomcat_pid> > thread_dump.txt ``` 2. 在转储文件中搜索: - `"ContainerBackgroundProcessor"`(Tomcat 引擎线程) - `"main"`(主初始化线程) - 阻塞状态:`BLOCKED`、`WAITING` 3. 典型死锁特征: ``` "main" #1 prio=5 os_prio=0 tid=0x00007f... nid=0x1e waiting for monitor entry [0x00007f...] java.lang.Thread.State: BLOCKED (on object monitor at com.example.DeadlockService.init(DeadlockService.java:42)) ``` ### 3. **配置优化** - **增加启动超时**(在 `conf/server.xml`): ```xml <Service name="Catalina"> <Executor name="tomcatThreadPool" maxThreads="200" minSpareThreads="4" startStopThreads="2"/> <Connector executor="tomcatThreadPool" port="8080" startupTimeout="300000"/> <!-- 5分钟 --> </Service> ``` - **禁用并行加载**(在 `conf/context.xml`): ```xml <Context parallelAnnotationScanning="false" /> ``` ### 4. **依赖冲突检测** 使用 Maven 检测冲突: ```bash mvn dependency:tree -Dincludes=javax.servlet:* ``` 检查输出中是否有多个 Servlet API 版本(如 servlet-api jakarta.servlet-api 冲突) ### 5. **日志分析** - 启用 DEBUG 日志(在 `conf/logging.properties`): ```properties org.apache.catalina.core.ContainerBase.[Catalina].level = FINE org.apache.catalina.core.StandardContext.level = FINEST ``` - 关键日志位置: ``` logs/catalina.out logs/localhost_<date>.log ``` ### 6. **最小化测试** 1. 移除所有 `webapps/*` 应用 2. 新建测试应用 `webapps/ROOT/index.jsp`: ```jsp <%@ page contentType="text/html;charset=UTF-8" %> <h1>Test App</h1> ``` 3. 逐步添加组件(先加 `web.xml`,再逐项添加 servlet/filter) ### 7. **版本回退** 如果确认是 Tomcat 9.0.99 的 bug: ```bash # 下载稳定版本 wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.85/bin/apache-tomcat-9.0.85.tar.gz ``` ### 常见根本原因统计 | 原因类型 | 占比 | 典型表现 | |----------|------|----------| | 初始化死锁 | 45% | 线程 BLOCKED/WATING | | 资源加载阻塞 | 30% | 文件/DB连接超时 | | 依赖冲突 | 15% | ClassNotFoundException | | Tomcat bug | 10% | 空指针异常 | > **重要提示**:当问题解决后,建议在 `contextInitialized()` 方法中添加进度日志[^1]: > ```java > public void contextInitialized(ServletContextEvent sce) { > log.info("▶️ 初始化阶段1:加载配置"); > // 阶段1代码... > log.info("▶️ 初始化阶段2:连接数据库"); > // 阶段2代码... > } > ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值