如何在基于 servlet 的 Web 应用程序中运行后台任务?

Posted

技术标签:

【中文标题】如何在基于 servlet 的 Web 应用程序中运行后台任务?【英文标题】:How to run a background task in a servlet based web application? 【发布时间】:2011-06-09 03:13:01 【问题描述】:

我正在使用 Java,我想保持一个 servlet 在我的应用程序中持续运行,但我不知道如何去做。我的 servlet 有一种方法可以每天计算数据库中的用户计数以及整个数据库中的用户总数。所以我想让 servlet 持续运行。

【问题讨论】:

你是什么意思,“持续运行”? 连续运行是什么意思?只要您的应用服务器运行,它就会运行 我不明白为什么它必须连续运行...如果有人想要“用户数”,那么他们会调用您的 servlet 方法,然后您将其提供给他们? @***foe 实际上我希望每天都有用户计数,因此我必须每天手动运行 servlet,所以我不想这样做,而是要连续运行 servlet。所以我不需要运行servlet 每天。 @pritsag:一个 servlet 用于服务用户请求,而不是运行批处理作业。 【参考方案1】:

您的问题是您误解了servlet 的用途。它旨在处理 HTTP 请求,仅此而已。您只需要一个每天运行一次的后台任务。

EJB 可用吗?使用@Schedule

如果您的环境恰好支持 EJB(即真正的 Java EE 服务器,例如 WildFly、JBoss、TomEE、Payara、GlassFish 等),请改用 @Schedule。以下是一些示例:

@Singleton
public class BackgroundJobManager 

    @Schedule(hour="0", minute="0", second="0", persistent=false)
    public void someDailyJob() 
        // Do your job here which should run every start of day.
    

    @Schedule(hour="*/1", minute="0", second="0", persistent=false)
    public void someHourlyJob() 
        // Do your job here which should run every hour of day.
    

    @Schedule(hour="*", minute="*/15", second="0", persistent=false)
    public void someQuarterlyJob() 
        // Do your job here which should run every 15 minute of hour.
    

    @Schedule(hour="*", minute="*", second="*/5", persistent=false)
    public void someFiveSecondelyJob() 
        // Do your job here which should run every 5 seconds.
    

 

是的,仅此而已。容器会自动拾取并管理它。

EJB 不可用?使用ScheduledExecutorService

如果您的环境不支持 EJB(即,您使用的不是真正的 Java EE 服务器,而是准系统 servletcontainer,例如 Tomcat、Jetty 等),请使用 ScheduledExecutorService。这可以由ServletContextListener 发起。这是一个启动示例:

@WebListener
public class BackgroundJobManager implements ServletContextListener 

    private ScheduledExecutorService scheduler;

    @Override
    public void contextInitialized(ServletContextEvent event) 
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new SomeDailyJob(), 0, 1, TimeUnit.DAYS);
        scheduler.scheduleAtFixedRate(new SomeHourlyJob(), 0, 1, TimeUnit.HOURS);
        scheduler.scheduleAtFixedRate(new SomeQuarterlyJob(), 0, 15, TimeUnit.MINUTES);
        scheduler.scheduleAtFixedRate(new SomeFiveSecondelyJob(), 0, 5, TimeUnit.SECONDS);
    

    @Override
    public void contextDestroyed(ServletContextEvent event) 
        scheduler.shutdownNow();
    


工作类如下所示:

public class SomeDailyJob implements Runnable 

    @Override
    public void run() 
        // Do your daily job here.
    


public class SomeHourlyJob implements Runnable 

    @Override
    public void run() 
        // Do your hourly job here.
    


public class SomeQuarterlyJob implements Runnable 

    @Override
    public void run() 
        // Do your quarterly job here.
    


public class SomeFiveSecondelyJob implements Runnable 

    @Override
    public void run() 
        // Do your quarterly job here.
    


永远不要考虑在基于 Java EE / Servlet 的环境中使用 java.util.Timer/java.lang.Thread

最后但同样重要的是,永远不要在 Java EE 中直接使用 java.util.Timer 和/或 java.lang.Thread。这是麻烦的秘诀。可以在这个与 JSF 相关的关于同一问题的答案中找到详细的解释:Spawning threads in a JSF managed bean for scheduled tasks using a timer。

【讨论】:

@BalucS 谢谢先生,您的解决方案帮助了我,我了解了 ScheduledExecutorService,这对我来说是新的,因为我是 java 新手。再次感谢您。 @BalusC : 类 UpdateCounts 应该放在 web.xml 的什么位置? @Ashwin web.xml 是Deployment Descriptor。 UpdateCount 类与部署无关,因此不必放在 web.xmlScheduledExecutorService 的一个关键问题:确保在您的执行程序中捕获所有异常。如果您的run 方法出现异常,则执行程序会静默停止执行。这是一个功能而不是错误。阅读文档并通过谷歌搜索进行学习。 @Agi:如果没有按照示例正确调用 scheduler.shutdownNow(),就会发生这种情况。如果这没有被调用,那么调度线程确实会继续运行。【参考方案2】:

我建议使用像quartz 这样的库来定期运行任务。 servlet 究竟做了什么?它会向您发送报告?

【讨论】:

是的,它给了我每天创建的用户数以及我的数据库中的总用户数。 嗯?你能描述一下你系统的完整架构吗?我迷路了。 @Twister 我是 Java 新手,正处于学习阶段,先生,真的不太了解 servlet。 问题不在于 servlet。你说的应用是什么? (ps:删除你的cmets是个坏主意,尤其是我回答过的cmets) @twister 当用户点击应用程序时,他将获得所有详细信息,例如今天创建了多少用户,到目前为止创建了多少用户等。我想在后台连续运行 servlet以便用户可以获取更新。我知道这不是正确的解释。(ps:我知道这是一个坏主意。抱歉。)【参考方案3】:

实现两个类,在main中调用startTask()

public void startTask()

    // Create a Runnable
    Runnable task = new Runnable() 
        public void run() 
            while (true) 
                runTask();
            
        
    ;

    // Run the task in a background thread
    Thread backgroundThread = new Thread(task);
    // Terminate the running thread if the application exits
    backgroundThread.setDaemon(true);
    // Start the thread
    backgroundThread.start();


public void runTask()

    try 
        // do something...         
        Thread.sleep(1000);

     catch (InterruptedException e) 
        e.printStackTrace();
    

【讨论】:

这绝对不是在 Web 应用程序中执行此操作的方法 - 请查看上面@BalusC 的答案 - 他在这里是正确的,我会说你可以相信他的所有答案。跨度> 【参考方案4】:

您可以使用 cron4j。 http://www.sauronsoftware.it/projects/cron4j/manual.php

【讨论】:

【参考方案5】:

在可能有多个非 jee 容器运行的生产系统中。使用诸如 Quartz 调度程序之类的企业调度程序,该调度程序可以配置为使用数据库进行任务管理。

【讨论】:

以上是关于如何在基于 servlet 的 Web 应用程序中运行后台任务?的主要内容,如果未能解决你的问题,请参考以下文章

在基于 servlet 的应用程序中放置以及如何读取配置资源文件?

对于基于 Servlet 的 Java Web 应用程序,我真的需要 web.xml 吗?

web开发之Servlet 一

TomcatServlet 工作原理解析

WebApplicationInitializer究 Spring 3.1之无web.xml式 基于代码配置的servlet3.0应用

Tomcat基于Servlet的无文件webshell的相关技术研究