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


97

我正在使用Java,但我想保持servlet在我的应用程序中连续运行,但是我没有得到如何做的信息。我的servlet有一种方法,可以每天提供数据库中用户的数量以及整个数据库中用户的总数。因此,我想为此继续保持servlet的运行。


你是什​​么意思,“持续运行”?
skaffman 2011年

1
连续运行是什么意思?只要您的应用服务器运行
它将一直

2
我不明白为什么它必须连续运行...如果有人想要“用户数”,那么他们将调用您的servlet方法,然后您将其提供给他们?
trojanfoe 2011年

@trojanfoe实际上,我希望每天都有用户计数,因此,我将不得不每天手动运行servlet,而不是那样我想连续运行servlet。因此,我不需要每天运行servlet。
pritsag 2011年

1
@pritsag:那里有一个servlet来服务用户请求,而不是运行批处理作业。
skaffman 2011年

Answers:


217

您的问题是您误解了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服务器,而是使用诸如Tomcat,Jetty等的准系统servlet容器),请使用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在Java EE中使用。这是麻烦的秘诀。在与JSF相关的同一问题上可以找到详细的解释:使用timer在JSF管理的bean中生成线程以执行计划的任务


9
@BalucS谢谢您,先生,您的解决方案帮助了我,我了解了ScheduledExecutorService,这对我来说是java的新手。再次感谢您。
pritsag 2011年

@BalusC:将类UpdateCounts放在web.xml中的什么位置?
Ashwin 2012年

1
@Ashwin web.xml是一个部署描述符UpdateCount类与部署无关,因此不必将其放在web.xml中
informatik01

12
一个关键问题ScheduledExecutorService:确保在执行程序中捕获所有异常。如果从您的run方法中逃脱了一个异常,则执行程序将静默停止执行。这是一个功能而不是错误。阅读文档并进行一些谷歌搜索。
罗勒·布尔克

1
@Agi:如果scheduler.shutdownNow()未按照示例正确调用,则会发生这种情况。如果未调用此函数,则调度线程确实将继续运行。
BalusC

4

我建议使用石英等库,以便定期运行任务。servlet真正做什么?它会向您发送报告吗?


是的,它为我提供了每天创建的用户数以及数据库中的总用户数。
pritsag 2011年

1
hu?您能否描述系统的完整架构。我迷路了。
Twister

@Twister是Java的新手,处于学习阶段,先生,对servlet的了解并不多。
pritsag 2011年

问题不在于servlet。您正在谈论什么应用程序?(ps:删除您的评论,尤其是我回答的评论是一个坏主意)
Twister

@twister,当用户点击该应用程序时,他将获得所有详细信息,例如今天创建了多少用户,到现在为止有多少用户被激怒等等。我想连续在后台运行servlet,以便用户可以获取更新。我硝酸钾,这不是正确的explation(PS:我知道它是一个坏idea.sorry)。
pritsag

3

实行两班,并呼吁startTask()main

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();
    }
}

3
这绝对不是在Web应用程序中执行此操作的方法-而是查看@BalusC的上述答案-他在这里是正确的,我想您可以相信他的所有答案。
Yoshiya


0

在可能有多个运行的非Jee容器的生产系统中。使用非企业调度程序,例如Quartz调度程序,可以将其配置为使用数据库来执行任务管理。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.