【问题标题】:How to run a background task in a servlet based web application?如何在基于 servlet 的 Web 应用程序中运行后台任务?
【发布时间】:2020-11-27 18:58:01
【问题描述】:

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

【问题讨论】:

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

标签: multithreading jakarta-ee servlets background-process scheduledexecutorservice


【解决方案1】:

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

【讨论】:

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

您的问题是您误解了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.xmlDeployment DescriptorUpdateCount 类与部署无关,因此不必放在 web.xml
  • ScheduledExecutorService 的一个关键问题:确保在您的执行程序中捕获所有异常。如果您的run 方法出现异常,则执行程序会静默停止执行。这是一个功能而不是错误。阅读文档并通过谷歌搜索进行学习。
  • @Agi:如果没有按照示例正确调用 scheduler.shutdownNow(),就会发生这种情况。如果不调用,那么调度线程确实会继续运行。
【解决方案3】:

【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      实现两个类,在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 的答案 - 他在这里是正确的,我会说你可以相信他的所有答案。跨度>
      猜你喜欢
      • 1970-01-01
      • 2016-05-04
      • 1970-01-01
      • 2021-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多