Làm thế nào để chạy một tác vụ nền trong một ứng dụng web dựa trên servlet?


97

Tôi đang sử dụng Java và tôi muốn giữ cho một servlet liên tục chạy trong ứng dụng của mình, nhưng tôi không biết cách thực hiện. Servlet của tôi có một phương thức cung cấp số lượng người dùng từ cơ sở dữ liệu hàng ngày cũng như tổng số người dùng từ toàn bộ cơ sở dữ liệu. Vì vậy, tôi muốn giữ cho servlet chạy liên tục cho điều đó.


Ý bạn là gì, "chạy liên tục"?
skaffman

1
ý bạn là gì khi chạy liên tục? Nó sẽ chạy càng lâu càng máy chủ chạy ứng dụng của bạn
fmucar

2
Tôi không hiểu tại sao nó phải chạy liên tục ... nếu ai đó muốn 'số lượng người dùng' thì họ gọi phương thức servlet của bạn và bạn đưa nó cho họ?
trojanfoe

@trojanfoe Trên thực tế, tôi muốn số tiền sử dụng hàng ngày, vì vậy tôi sẽ phải chạy servlet theo cách thủ công hàng ngày vì vậy thay vì làm điều đó tôi muốn chạy servlet liên tục. vì vậy tôi sẽ không cần phải chạy servlet hàng ngày.
pritsag

1
@pritsag: Một servlet ở đó để phục vụ các yêu cầu của người dùng, không phải để chạy các công việc hàng loạt.
skaffman

Câu trả lời:


217

Vấn đề của bạn là bạn hiểu sai mục đích của servlet . Nó có ý định hoạt động trên các yêu cầu HTTP, không có gì hơn. Bạn chỉ muốn một nhiệm vụ nền chạy một lần hàng ngày.

EJB có sẵn? Sử dụng@Schedule

Nếu môi trường của bạn hỗ trợ EJB (tức là một máy chủ Java EE thực như WildFly, JBoss, TomEE, Payara, GlassFish, v.v.), thì hãy sử dụng @Schedulethay thế. Dưới đây là một số ví dụ:

@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.
    }

} 

Vâng, đó thực sự là tất cả. Hộp chứa sẽ tự động nhận và quản lý nó.

EJB không khả dụng? Sử dụngScheduledExecutorService

Nếu môi trường của bạn không hỗ trợ EJB (tức là bạn không sử dụng máy chủ Java EE thực mà không phải là máy chủ servletcontainer cơ bản như Tomcat, Jetty, v.v.), thì hãy sử dụng ScheduledExecutorService. Điều này có thể được bắt đầu bởi a ServletContextListener. Đây là một ví dụ khởi động:

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

}

Nơi các lớp việc làm trông như thế này:

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.
    }

}

Đừng bao giờ nghĩ đến việc sử dụng java.util.Timer/ java.lang.Threadtrong môi trường dựa trên Java EE / Servlet

Cuối cùng nhưng không kém phần quan trọng, không bao giờ sử dụng trực tiếp java.util.Timervà / hoặc java.lang.Threadtrong Java EE. Đây là công thức cho rắc rối. Có thể tìm thấy một lời giải thích phức tạp trong câu trả lời liên quan đến JSF này cho cùng một câu hỏi: Sinh ra các luồng trong bean được quản lý JSF cho các tác vụ đã lên lịch sử dụng bộ đếm thời gian .


9
@BalucS.
pritsag

@BalusC: Lớp UpdateCounts nên được đặt ở đâu trong web.xml?
Ashwin

1
@Ashwin web.xml là một Trình mô tả Triển khai . Lớp UpdateCount không liên quan với việc triển khai, vì vậy nó không nhất thiết phải được đặt trong web.xml
informatik01

12
Một vấn đề quan trọng với a ScheduledExecutorService: Đảm bảo nắm bắt tất cả các ngoại lệ trong trình thực thi của bạn. Nếu một ngoại lệ thoát khỏi runphương thức của bạn , trình thực thi sẽ im lặng ngừng thực thi. Đây là một tính năng, không phải là một lỗi. Đọc tài liệu và nghiên cứu với một số googling.
Basil Bourque

1
@Agi: điều đó sẽ xảy ra nếu scheduler.shutdownNow()không được gọi đúng theo ví dụ. Nếu điều này không được gọi, thì chuỗi lịch trình thực sự sẽ tiếp tục chạy.
BalusC

4

Tôi sẽ đề xuất sử dụng một thư viện như thạch anh để chạy tác vụ trong các khoảng thời gian đều đặn. Servlet thực sự làm gì? Nó gửi cho bạn một báo cáo?


vâng, nó cung cấp cho tôi số lượng người dùng được tạo mỗi ngày và cũng là số lượng tổng số người dùng trong cơ sở dữ liệu của tôi.
pritsag

1
huuu? Bạn có thể mô tả kiến ​​trúc ĐẦY ĐỦ của hệ thống của bạn. Tôi bị lạc.
Twister

@Twister Tôi mới sử dụng java và đang trong giai đoạn tìm hiểu thưa ông và thực sự không biết nhiều về các servlet.
pritsag

Vấn đề không phải là về servlet. Ứng dụng bạn đang nói về là gì? (ps: xóa bình luận của bạn là một ý kiến ​​tồi, đặc biệt là những bình luận mà tôi đã trả lời)
Twister

@twister khi người dùng truy cập ứng dụng, anh ta sẽ nhận được tất cả các chi tiết như số lượng người dùng được tạo ngày hôm nay, số lượng người dùng được truy cập cho đến bây giờ, v.v. và tôi muốn chạy servlet chạy trong nền liên tục để người dùng có thể nhận được các bản cập nhật .I Knô đây không phải là explation thích hợp (ps: tôi biết đó là một idea.sorry xấu cho điều đó.).
pritsag

3

Triển khai hai lớp và gọi startTask()vào 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
Đây chắc chắn KHÔNG phải là cách để làm điều đó trong một ứng dụng web - thay vào đó, hãy xem câu trả lời ở trên của @BalusC - anh ấy đúng ở đây và tôi muốn nói rằng bạn có thể tin tưởng tất cả các câu trả lời của anh ấy.
Yoshiya


0

Trong một hệ thống sản xuất có thể có nhiều thùng chứa không jee đang chạy. Sử dụng bộ lập lịch anot doanh nghiệp như bộ lập lịch Quartz có thể được cấu hình để sử dụng cơ sở dữ liệu cho maamgememt nhiệm vụ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.