如何在Spring-Boot中启动(和最终停止)守护线程?

3

我正在编写一个Spring-Boot应用程序,监控一个目录并处理添加到其中的文件。我通过在我的Application类中创建一个ApplicationRunner来启动一个线程,调用一个使用@Async注解的方法:

@SpringBootApplication
@EnableAsync
public class Application {

    @Autowired
    private DirectoryMonitorService directoryMonitorService;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public ApplicationRunner startDirectoryMonitorService() {
        return args -> directoryMonitorService.monitorSourceDirectoty();
    }
}

这是 DirectoryMonitorService 的代码,其中有一个被标记为 @Async 的方法:

@Service
public class DirectoryMonitorService {

    private static final Logger logger = LogManager.getLogger(DirectoryMonitorService.class);

    @Value("${timeout}")
    private long timeout;

    @Autowired
    private WatchService watchService;

    @Async
    public void monitorSourceDirectoty() {
        while (true) {
            WatchKey watchKey;
            try {
                watchKey = watchService.poll(timeout, TimeUnit.SECONDS);
            } catch (ClosedWatchServiceException | InterruptedException e) {
                logger.error("Exception occured while polling from source file", e);
                return;
            }

            // process the WatchEvents

            if (!watchKey.reset()) {
                break;
            }
        }
    }
}

最后这里是我创建ThreadPoolTaskExecutor的地方:

public class AsyncConfig extends AsyncConfigurerSupport {

    private static final Logger logger = LogManager.getLogger(AsyncConfig.class);

    private static final String THREAD_NAME_PREFIX = "Parser-";

    @Value("${corePoolSize}")
    public int corePoolSize;

    @Value("${maxPoolSize}")
    public int maxPoolSize;

    @Value("${queueCapacity}")
    public int queueCapacity;

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        executor.initialize();

        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (Throwable ex, Method method, Object... params) -> {
            logger.error("Exception message - " + ex.getMessage());
            logger.error("Method name - " + method.getName());
            for (Object param : params) {
                logger.error("Parameter value - " + param);
            }
        };
    }
}

我觉得这不是最优雅的启动主线程的方式。有没有更好的解决方案?

另外,我宁愿用一个布尔变量替换 while (true),当 Spring-Boot 关闭时我可以将其设置为 false。有人知道我需要实现哪个接口吗?


我会选择启动一个普通的线程,而不是滥用@Async。并且根据https://dev59.com/MWw15IYBdhLWcg3wcrdC#6603443中的建议使用`interrupt()`来中断它。 - zapl
有人知道吗,@Async是否创建守护线程? - kayesh parvez
1个回答

0

如果您想要一个非常简单的实现且不需要更可靠的内容,那么这是正确的。

对于较短的任务,可以使用@Async,但其重启等方面的能力非常有限。

此外,@Async将在每个观察序列激活时创建单独的线程,并且会使线程池不堪重负并开始抛出异常。如果您有长时间运行的任务,则会非常明显,例如:

// 处理WatchEvents

除此之外,您的实现是正确的(在我看来)。

一些建议(如果您想让事情变得有趣/复杂):

因此,您可以使用某种持久性机制跟踪文件,并触发解耦合批处理(可以使用Spring Batch)来处理执行,并将这些批处理获取到单独的UI或其他东西中,在那里您可以停止、启动、恢复每个这些批处理过程的UI。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接