จะรันงานพื้นหลังในเว็บแอปพลิเคชันที่ใช้ servlet ได้อย่างไร


97

ฉันใช้ Java และฉันต้องการให้ servlet ทำงานอย่างต่อเนื่องในแอปพลิเคชันของฉัน แต่ฉันไม่เข้าใจวิธีการทำ servlet ของฉันมีวิธีการที่ให้จำนวนผู้ใช้จากฐานข้อมูลในแต่ละวันรวมทั้งจำนวนผู้ใช้ทั้งหมดจากฐานข้อมูลทั้งหมด ดังนั้นฉันต้องการให้ servlet ทำงานอย่างต่อเนื่องเพื่อสิ่งนั้น


"วิ่งอย่างต่อเนื่อง" หมายความว่าอย่างไร
skaffman

1
วิ่งอย่างต่อเนื่องหมายความว่าอย่างไร มันจะทำงานตราบเท่าที่เซิร์ฟเวอร์แอปของคุณทำงาน
fmucar

2
ฉันไม่เข้าใจว่าทำไมมันถึงต้องทำงานอย่างต่อเนื่อง ... ถ้ามีคนต้องการ 'จำนวนผู้ใช้' พวกเขาก็เรียกวิธี servlet ของคุณแล้วคุณให้มัน?
โทรจัน

@trojanfoe จริงๆแล้วฉันต้องการ usercount ทุกวันดังนั้นฉันจะต้องเรียกใช้ servlet ด้วยตนเองทุกวันดังนั้นแทนที่จะทำอย่างนั้นฉันต้องการเรียกใช้ servlet อย่างสม่ำเสมอดังนั้นฉันไม่จำเป็นต้องเรียกใช้ servlet ทุกวัน
pritsag

1
@pritsag: มี servlet เพื่อตอบสนองคำขอของผู้ใช้ไม่ใช่เพื่อเรียกใช้งานชุดงาน
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 จริง แต่เล่า servletcontainer เช่น Tomcat, ท่าเทียบเรือ, ฯลฯ ) 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 ถั่วจัดการสำหรับการใช้งานที่กำหนดเวลาการจับเวลา


9
@BalucS ขอบคุณครับโซลูชันของคุณช่วยฉันและฉันได้เรียนรู้เกี่ยวกับ ScheduledExecutorService ซึ่งเป็นสิ่งใหม่สำหรับฉันเพราะฉันเพิ่งเริ่มใช้ java ขอบคุณอีกครั้ง
pritsag

@BalusC: ชั้น UpdateCounts ควรอยู่ที่ไหนใน web.xml?
Ashwin

1
@Ashwin web.xmlเป็นคำอธิบายถึงการนำ คลาสUpdateCountไม่เกี่ยวข้องกับการปรับใช้ดังนั้นจึงไม่ต้องใส่ในweb.xml
informatik01

12
ปัญหาหนึ่งที่สำคัญกับScheduledExecutorService: ให้แน่ใจว่าจะจับทุกข้อยกเว้นในการปฏิบัติตามคำสั่งของคุณ หากมีข้อยกเว้นหนีไปจากrunเมธอดของคุณตัวดำเนินการจะหยุดดำเนินการโดยไม่โต้ตอบ นี่คือคุณสมบัติไม่ใช่จุดบกพร่อง อ่านเอกสารและศึกษากับ googling
Basil Bourque

1
@ Agi: จะเกิดขึ้นหากscheduler.shutdownNow()เรียกไม่ถูกต้องตามตัวอย่าง หากไม่ถูกเรียกใช้เธรดกำหนดการจะยังคงทำงานต่อไป
BalusC

4

ฉันขอแนะนำให้ใช้ไลบรารีเช่นควอตซ์เพื่อเรียกใช้งานในช่วงเวลาปกติ servlet ทำอะไรได้บ้าง? จะส่งรายงานให้คุณ?


ใช่มันให้จำนวนผู้ใช้ที่สร้างขึ้นต่อวันและจำนวนผู้ใช้ทั้งหมดในฐานข้อมูลของฉันด้วย
pritsag

1
ฮิ้ววว? คุณสามารถอธิบายสถาปัตยกรรมแบบเต็มของระบบของคุณได้ไหม ฉันหลงทาง.
Twister

@Twister ฉันยังใหม่กับ java และอยู่ในขั้นตอนการเรียนรู้และไม่สนใจเรื่อง servlets มากนัก
pritsag

ปัญหาไม่ได้เกี่ยวกับ servlet แอปพลิเคชันที่คุณกำลังพูดถึงคืออะไร? (PS: การลบความคิดเห็นของคุณเป็นความคิดที่ไม่ดีโดยเฉพาะความคิดเห็นที่ฉันตอบไป)
Twister

@twister เมื่อผู้ใช้จะเข้าสู่แอปพลิเคชันเขาจะได้รับรายละเอียดทั้งหมดเช่นจำนวนผู้ใช้ที่สร้างขึ้นในวันนี้จำนวนผู้ใช้ที่กระหายจนถึงขณะนี้เป็นต้นและฉันต้องการเรียกใช้ servlet ที่ทำงานในพื้นหลังอย่างสม่ำเสมอเพื่อให้ผู้ใช้สามารถรับการอัปเดตได้ ฉันรู้ว่านี่ไม่ใช่การอธิบายที่เหมาะสม (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 แทน - เขาถูกต้องที่นี่และฉันจะบอกว่าคุณสามารถเชื่อถือคำตอบทั้งหมดของเขาได้
Yoshiya


0

ในระบบการผลิตที่อาจมีคอนเทนเนอร์ที่ไม่ใช่ jee หลายตัวทำงานอยู่ ใช้ตัวกำหนดตารางเวลาขององค์กร anot เช่นตัวกำหนดตารางเวลาควอตซ์ซึ่งสามารถกำหนดค่าให้ใช้ฐานข้อมูลสำหรับงาน maamgememt

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.