Як запустити фонове завдання у веб-програмі на основі сервлету?


97

Я використовую Java, і я хочу, щоб сервлет постійно працював у моїй програмі, але я не розумію, як це зробити. Мій сервлет має метод, який щоденно підраховує кількість користувачів із бази даних, а також загальну кількість користувачів з усієї бази даних. Тому я хочу, щоб сервлет постійно працював для цього.


Що ви маєте на увазі, "постійно працює"?
skaffman

1
що ти маєш на увазі під безперервним бігом? Він працюватиме до тих пір, поки працює ваш сервер додатків
fmucar

2
Я не розумію, чому він повинен працювати безперервно ... якщо хтось хоче "кількість користувачів", то вони викликають ваш метод сервлетів, і ви їм їх даєте?
trojanfoe

@trojanfoe Насправді я хочу щодня користувальницьку кількість, тому для цього мені доведеться запускати сервлет щодня вручну, тому замість того, щоб робити це, я хочу запускати сервлет безперервно. Тому мені не потрібно запускати сервлет щодня.
притсаг

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. Це може ініціювати a 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 для запланованих завдань за допомогою таймера .


9
@BalucS Дякую, сер, ваше рішення допомогло мені, і я дізнався про ScheduledExecutorService, який був для мене новим, як я новий для java.Дякую ще раз.
прицаг

@BalusC: Куди слід класти клас UpdateCounts у web.xml?
Ashwin

1
@Ashwin web.xml - це дескриптор розгортання . Клас UpdateCount не пов'язаний з розгортанням, тому його не потрібно поміщати в web.xml
informatik01

12
Одна з найважливіших проблем ScheduledExecutorService: Не забудьте зафіксувати всі винятки у вашому виконавці. Якщо виняток виходить із вашого runметоду, виконавець мовчки зупиняє виконання. Це особливість, а не помилка. Прочитайте документ і навчіться, погуглівши.
Василь Бурк

1
@Agi: це трапиться, якщо scheduler.shutdownNow()неправильно викликати згідно з прикладом. Якщо цього не викликати, тоді потік розкладу дійсно буде продовжувати працювати.
BalusC

4

Я б запропонував використовувати бібліотеку, як кварц, щоб регулярно виконувати завдання. Що насправді робить серветка? Він надсилає вам звіт?


так, це дає мені кількість користувачів, створених за день, а також загальну кількість користувачів у моїй базі даних.
притсаг

1
хууу? Чи можете ви описати ПОВНУ архітектуру вашої системи. Я загубився.
Твістер

@Twister я новачок у Java та на етапі навчання, сер, і насправді не знаю багато про сервлети.
притсаг

Проблема не в сервлеті. Про яку програму ви говорите? (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

У виробничій системі, яка може мати декілька запущених контейнерів, що не складаються з джинсів. Використовуйте не такий планувальник підприємств, як кварцовий планувальник, який можна налаштувати на використання бази даних для завдання maamgememt.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.