Java Tomcat冻结部署SpringBoot应用程序

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

我试图在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/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);
    }

}