Как запустить фоновую задачу в веб-приложении на основе сервлета?

97

Я использую Java и хочу, чтобы в моем приложении постоянно работал сервлет, но я не понимаю, как это сделать. У моего сервлета есть метод, который дает количество пользователей из базы данных на ежедневной основе, а также общее количество пользователей из всей базы данных. Поэтому я хочу, чтобы сервлет работал для этого постоянно.

Прицаг
источник
Что вы имеете в виду под «непрерывно работающим»?
skaffman
1
что вы имеете в виду под непрерывным бегом? Он будет работать, пока работает ваш сервер приложений
fmucar
2
Я не понимаю, почему он должен работать непрерывно ... если кто-то хочет «подсчет пользователей», они вызывают ваш метод сервлета, и вы даете его им?
trojanfoe
@trojanfoe На самом деле я хочу получать usercount ежедневно, поэтому для этого мне придется каждый день запускать сервлет вручную, поэтому вместо этого я хочу запускать сервлет постоянно. поэтому мне не нужно запускать сервлет каждый день.
pritsag
1
@pritsag: сервлет предназначен для обслуживания запросов пользователей, а не для выполнения пакетных заданий.
skaffman

Ответы:

217

Ваша проблема в том, что вы неправильно понимаете цель сервлета . Он предназначен для обработки 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 и т. Д.), Используйте 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.util.Timer/ java.lang.Threadв среде на основе Java EE / Servlet

И последнее, но не менее важное: никогда не используйте напрямую java.util.Timerи / или java.lang.Threadв Java EE. Это рецепт неприятностей. Подробное объяснение можно найти в этом относящемся к JSF ответе на тот же вопрос: создание потоков в управляемом JSF компоненте для запланированных задач с использованием таймера .

BalusC
источник
9
@BalucS Спасибо, сэр, ваше решение помогло мне, и я узнал о ScheduledExecutorService, который был для меня новым, поскольку я новичок в java. Еще раз спасибо.
pritsag
@BalusC: Где должен быть помещен класс UpdateCounts в web.xml?
Ashwin
1
@Ashwin web.xml - это дескриптор развертывания . Класс UpdateCount не связан с развертыванием, поэтому его не нужно помещать в web.xml
informatik01
12
Одна важная проблема с a ScheduledExecutorService: обязательно зафиксируйте все исключения в своем исполнителе. Если исключение ускользает из вашего runметода, исполнитель молча прекращает выполнение. Это функция, а не ошибка. Прочтите документ и изучите его, погуглив.
Basil Bourque
1
@Agi: это произойдет, если scheduler.shutdownNow()не будет правильно вызван в соответствии с примером. Если это не вызвано, то поток расписания действительно продолжит работу.
BalusC
4

Я бы посоветовал использовать такую ​​библиотеку, как quartz, для регулярного выполнения задачи. Что на самом деле делает сервлет? Он отправляет вам отчет?

Твистер
источник
да, он дает мне количество пользователей, созданных за день, а также общее количество пользователей в моей базе данных.
pritsag
1
хууу? Можете ли вы описать ПОЛНУЮ архитектуру вашей системы. Я потерялся.
Twister
@Twister Я новичок в java и на этапе обучения, сэр, и я действительно не очень разбираюсь в сервлетах.
pritsag
Проблема не в сервлете. О каком приложении вы говорите? (ps: удалять ваши комментарии - плохая идея, особенно комментарии, на которые я ответил)
Twister
@twister, когда пользователь попадет в приложение, он получит все подробности, например, сколько пользователей создано сегодня, сколько пользователей создано до сих пор и т. д., и я хочу постоянно запускать сервлет в фоновом режиме, чтобы пользователь мог получать обновления .Я знаю, что это неправильное объяснение. (Ps: я знаю, что это была плохая идея. Извините за это.)
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
Это определенно НЕ способ сделать это в веб-приложении - вместо этого посмотрите ответ @BalusC выше - здесь он прав, и я бы сказал, что вы можете доверять всем его ответам.
Йошия
0

В производственной системе, в которой может работать несколько контейнеров, не относящихся к jee. Используйте другой планировщик предприятия, например Quartz scheduler, который можно настроить для использования базы данных для задач maamgememt.

Джерил Кук
источник