Java Tomcat冻结部署SpringBoot应用程序
我试图在Tomcat 9中删除一些应用程序(4个spring boot web应用程序),我注意到其中有2个应用程序使Tomcat日志显示:Java Tomcat冻结部署SpringBoot应用程序,java,spring,tomcat,deployment,watchservice,Java,Spring,Tomcat,Deployment,Watchservice,我试图在Tomcat 9中删除一些应用程序(4个spring boot web应用程序),我注意到其中有2个应用程序使Tomcat日志显示: 17-Nov-2016 00:15:07.110 INFO [localhost-startStop-2] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive /Users/viruskimera/apache-tomcat-9.0.0.M11
17-Nov-2016 00:15:07.110 INFO [localhost-startStop-2] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive /Users/viruskimera/apache-tomcat-9.0.0.M11/webapps/ftpoutbound990-0.0.1-SNAPSHOT.war
它保持这种状态,只是部署它并没有显示部署已完成。
问题是,即使应用程序看起来没有完全部署,它仍然可以工作。
(我看到我的log4j条目和文件在监控文件夹中处理)
这些应用程序使用Java watchService监控2个不同的文件夹,代码如下:
包com.ftpoutbound990.monitor
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.ftpoutbound990.client.FtpoutboundApp;
@Component
public class MonitorDirectory {
final static Logger logger = Logger.getLogger(MonitorDirectory.class);
@Autowired
private FtpoutboundApp ftpoutboundapp;
@Value("${folder990}")
private String folder990;
public void startMonitoring() throws IOException, InterruptedException {
logger.info("INICIO DE MONITOREO DE ARCHIVOS 990");
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
Path faxFolder = Paths.get(folder990);
WatchService watchmyservice = FileSystems.getDefault().newWatchService();
faxFolder.register(watchmyservice, StandardWatchEventKinds.ENTRY_CREATE);
boolean valid = true;
do {
try {
WatchKey watchKey = watchmyservice.take();
for (WatchEvent event : watchKey.pollEvents()) {
WatchEvent.Kind kind = event.kind();
if (StandardWatchEventKinds.ENTRY_CREATE.equals(event.kind())) {
String fileName = event.context().toString();
logger.info("ARCHIVO NUEVO:" + fileName);
boolean isGrowing = false;
Long initialWeight = new Long(0);
Long finalWeight = new Long(0);
Path child = faxFolder.resolve(folder990 + fileName);
do {
initialWeight = child.toFile().length();
Thread.sleep(1000);
finalWeight = child.toFile().length();
isGrowing = initialWeight < finalWeight;
logger.info("AUN COPIANDO ARCHIVO:" + fileName);
} while (isGrowing);
logger.info("LISTO ARCHIVO:" + fileName);
getFile(fileName);
}
}
valid = watchKey.reset();
// Thread.sleep(1000 * 10);
} catch (InterruptedException | ClosedWatchServiceException e) {
//watchmyservice.close();
Thread.currentThread().interrupt();
}
} while (valid);
}
}
public void getFile(String fileName) throws IOException {
File file = new File(folder990 + fileName);
ftpoutboundapp.createGateway(file);
}
}
向盲人开枪 对代码的简单回顾表明,如果在try catch中抛出异常,布尔标志
valid
将保持true
,从而导致无限循环
因此,在
catch
块的主体中将标志设置为false tomcat部署程序线程似乎在调用startMonitoring
方法。正如预期的那样,此方法永远不会返回,因此部署不会完成
要解决此问题,需要在单独(或后台)线程中运行监视方法
下面的重构示例可能会有所帮助
除此之外,WatcherService实例的单个实例可用于监视多个目录
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MonitorDirectory {
final static Logger logger = Logger.getLogger(MonitorDirectory.class);
@Autowired
private FtpoutboundApp ftpoutboundapp;
@Value("${folder990}")
private String folder990;
private WatchService watchService;
private ExecutorService eventLoop = Executors.newFixedThreadPool(1);
@PostConstruct
public void init() throws IOException, InterruptedException {
watchService = FileSystems.getDefault().newWatchService();
eventLoop.submit((Runnable) () -> {
try {
startMonitoring();
} catch (Exception e) {
logger.error("ERROR...", e);
}
});
}
@PreDestroy
public void destroy() throws IOException {
eventLoop.shutdownNow();
if (watchService != null) {
watchService.close();
}
}
public void startMonitoring() throws IOException, InterruptedException {
logger.info("INICIO DE MONITOREO DE ARCHIVOS 990");
Path faxFolder = Paths.get(folder990);
WatchKey watchKey = faxFolder.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
boolean valid = true;
do {
for (WatchEvent<?> event : watchKey.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (StandardWatchEventKinds.ENTRY_CREATE.equals(kind)) {
String fileName = event.context().toString();
logger.info("ARCHIVO NUEVO:" + fileName);
boolean isGrowing = false;
Long initialWeight = new Long(0);
Long finalWeight = new Long(0);
Path child = faxFolder.resolve(folder990 + fileName);
do {
initialWeight = child.toFile().length();
Thread.sleep(1000);
finalWeight = child.toFile().length();
isGrowing = initialWeight < finalWeight;
logger.info("AUN COPIANDO ARCHIVO:" + fileName);
} while (isGrowing);
logger.info("LISTO ARCHIVO:" + fileName);
getFile(fileName);
}
valid = watchKey.reset();
}
} while (valid);
}
public void getFile(String fileName) throws IOException {
File file = new File(folder990 + fileName);
ftpoutboundapp.createGateway(file);
}
}
导入java.io.File;
导入java.io.IOException;
导入java.nio.file.FileSystems;
导入java.nio.file.Path;
导入java.nio.file.path;
导入java.nio.file.StandardWatchEventTypes;
导入java.nio.file.WatchEvent;
导入java.nio.file.WatchKey;
导入java.nio.file.WatchService;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入javax.annotation.PostConstruct;
导入javax.annotation.PreDestroy;
导入org.apache.log4j.Logger;
导入org.springframework.beans.factory.annotation.Autowired;
导入org.springframework.beans.factory.annotation.Value;
导入org.springframework.stereotype.Component;
@组成部分
公共类监视器目录{
最终静态记录器=Logger.getLogger(MonitorDirectory.class);
@自动连线
私人FtpoutboundApp FtpoutboundApp;
@值(${folder990}”)
私有字符串folder990;
私人值班服务;
私有ExecutorService eventLoop=Executors.newFixedThreadPool(1);
@施工后
public void init()引发IOException,InterruptedException{
watchService=FileSystems.getDefault().newWatchService();
eventLoop.submit((可运行)(->{
试一试{
开始监视();
}捕获(例外e){
记录器错误(“错误…”,e);
}
});
}
@发情前期
public void destroy()引发IOException{
eventLoop.shutdownNow();
if(watchService!=null){
watchService.close();
}
}
public void startMonitoring()引发IOException、InterruptedException{
logger.info(“INICIO DE MONITOREO DE ARCHIVOS 990”);
Path faxFolder=Path.get(folder990);
WatchKey WatchKey=faxFolder.register(watchService,StandardWatchEventKinds.ENTRY\u CREATE);
布尔有效=真;
做{
for(WatchEvent事件:watchKey.pollEvents()){
WatchEvent.Kind-Kind=event.Kind();
if(StandardWatchEventTypes.ENTRY_CREATE.equals(种类)){
字符串文件名=event.context().toString();
logger.info(“ARCHIVO NUEVO:+fileName”);
布尔值=假;
长初始重量=新长(0);
长最终重量=新长(0);
Path child=faxFolder.resolve(folder990+文件名);
做{
initialWeight=child.toFile().length();
睡眠(1000);
finalWeight=child.toFile().length();
isGrowing=初始重量<最终重量;
logger.info(“AUN COPIANDO ARCHIVO:+fileName”);
}同时(正在成长);
logger.info(“LISTO ARCHIVO:+fileName”);
getFile(文件名);
}
valid=watchKey.reset();
}
}有效期;
}
public void getFile(字符串文件名)引发IOException{
文件=新文件(folder990+文件名);
ftpoutboundapp.createGateway(文件);
}
}
你能检查一下下面的几件事吗
组件MonitorDirectory在应用程序中只调用一次。如果不是,您可能需要将“eventLoop”声明为静态,并在PostConstruct中检查初始化,以避免多次初始化和调用
此外,executorservice是使用threadpool 1初始化的,如果应用程序中初始化了多个MonitorDirectory对象,这可能会导致部署冻结
服务器环境中的文件大小是否有可能缩小。这可能会导致“isGrowing”检查出现无限循环 我正在尝试这一点看看我的编辑1,这是一个本地运行,但我担心它在服务器上会是相同的。我应用了您的代码并冻结了相同的代码,然后关闭tomcat并显示:查看行严重错误:28-Dec-2016 17:57:27.664,它显示等待部署完成的错误。我甚至尝试将spring版本从v1.4.1.RELEASE更改为v1.3.6.RELEASE,因为使用v1.4.1.RELEASE的1个应用程序部署正确,但同样的情况也发生了。顺便说一句,我注意到java进程的内存消耗增加。我假设您的startMonitoring仅从后台线程调用。另外,在停止tomcat服务器时出现超时异常看起来有点奇怪(在while条件中添加Thread.interrupted()条件后,可以尝试一下吗?例如while(valid&&!Thread.interrupted())。看起来相同,请注意,即使部署未完成,应用程序也始终运行,因为应用程序在log4j文件中显示开始监视消息,但在此之后
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MonitorDirectory {
final static Logger logger = Logger.getLogger(MonitorDirectory.class);
@Autowired
private FtpoutboundApp ftpoutboundapp;
@Value("${folder990}")
private String folder990;
private WatchService watchService;
private ExecutorService eventLoop = Executors.newFixedThreadPool(1);
@PostConstruct
public void init() throws IOException, InterruptedException {
watchService = FileSystems.getDefault().newWatchService();
eventLoop.submit((Runnable) () -> {
try {
startMonitoring();
} catch (Exception e) {
logger.error("ERROR...", e);
}
});
}
@PreDestroy
public void destroy() throws IOException {
eventLoop.shutdownNow();
if (watchService != null) {
watchService.close();
}
}
public void startMonitoring() throws IOException, InterruptedException {
logger.info("INICIO DE MONITOREO DE ARCHIVOS 990");
Path faxFolder = Paths.get(folder990);
WatchKey watchKey = faxFolder.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
boolean valid = true;
do {
for (WatchEvent<?> event : watchKey.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (StandardWatchEventKinds.ENTRY_CREATE.equals(kind)) {
String fileName = event.context().toString();
logger.info("ARCHIVO NUEVO:" + fileName);
boolean isGrowing = false;
Long initialWeight = new Long(0);
Long finalWeight = new Long(0);
Path child = faxFolder.resolve(folder990 + fileName);
do {
initialWeight = child.toFile().length();
Thread.sleep(1000);
finalWeight = child.toFile().length();
isGrowing = initialWeight < finalWeight;
logger.info("AUN COPIANDO ARCHIVO:" + fileName);
} while (isGrowing);
logger.info("LISTO ARCHIVO:" + fileName);
getFile(fileName);
}
valid = watchKey.reset();
}
} while (valid);
}
public void getFile(String fileName) throws IOException {
File file = new File(folder990 + fileName);
ftpoutboundapp.createGateway(file);
}
}