2014-07-21 17 views
6

Tôi đến từ một nền Perl và đang viết ứng dụng web Java MVC đầu tiên của tôi bằng cách sử dụng Spring.Quy trình nền Spring MVC

Ứng dụng web của tôi cho phép người dùng gửi các đơn đặt hàng mà ứng dụng xử lý đồng bộ bằng cách gọi dịch vụ SOAP của bên thứ ba. Giai đoạn tiếp theo của dự án là cho phép người dùng gửi các đơn đặt hàng số lượng lớn (ví dụ: CSV chứa 500 hàng) và xử lý chúng không đồng bộ. Dưới đây là một đoạn của bộ điều khiển hiện tại của tôi:

@Controller 
@Service 
@RequestMapping(value = "/orders") 
public class OrderController { 

    @Autowired 
    OrderService orderService; 

    @RequestMapping(value="/new", method = RequestMethod.POST) 
    public String processNewOrder(@ModelAttribute("order") Order order, Map<String, Object> map) { 

     OrderStatus orderStatus = orderService.processNewOrder(order); 

     map.put("orderStatus", orderStatus); 

     return "new"; 
    } 
} 

tôi có kế hoạch để tạo ra một mới @RequestMapping để đối phó với các CSV đến và sửa đổi các OrderService để có thể phá vỡ các CSV ra và kéo dài lệnh cá nhân đến cơ sở dữ liệu.

Câu hỏi của tôi là: cách tiếp cận tốt nhất để tạo công nhân nền trong ứng dụng MVC Spring là gì? Lý tưởng nhất là tôi sẽ có 5 chủ đề xử lý các đơn đặt hàng này và rất có thể từ hàng đợi. Tôi đã đọc về @Async hoặc gửi Runnable đến một hạt đậu SimpleAsyncTaskExecutor và không chắc chắn nên đi đường nào. Một số ví dụ sẽ thực sự giúp tôi.

+0

Lưu ý phụ: chú thích '@ Service' là dự phòng tại đây. – sp00m

+0

Điều gì sẽ xảy ra nếu quá trình nền không thành công? Aka, tôi tải lên một tệp và quá trình nhập không thành công vì một số lý do. Chuyện gì xảy ra sau đó? Nếu bạn cần để có thể khởi động lại quá trình nhập, Spring Batch có thể có ý nghĩa. Nếu không, có một số tùy chọn khác. –

Trả lời

1

Tôi nghĩ Spring Batch là quá mức cần thiết và không thực sự là những gì bạn đang tìm kiếm. Đó là nhiều hơn cho xử lý hàng loạt như viết tất cả các đơn đặt hàng vào một tập tin sau đó xử lý tất cả cùng một lúc, trong khi điều này có vẻ giống như xử lý không đồng bộ, nơi bạn chỉ muốn có một 'hàng đợi' của công việc và xử lý nó theo cách đó.

Nếu đây thực sự là trường hợp, tôi sẽ xem xét sử dụng mô hình pub/sub bằng JMS. Có một số nhà cung cấp JMS, ví dụ Apache ActiveMQ hoặc Pivotal RabitMQ. Về bản chất, OrderService sẽ chia CSV thành các đơn vị công việc, đẩy chúng vào hàng đợi JMS và bạn sẽ có nhiều thiết lập Người tiêu dùng để đọc từ Hàng đợi và thực hiện tác vụ công việc. Có rất nhiều cách để cấu hình này, nhưng tôi chỉ đơn giản sẽ tạo một lớp để giữ các luồng công nhân của bạn và làm cho số lượng các luồng có thể cấu hình được. Các lợi ích khác được thêm vào ở đây là:

  1. Bạn có thể mã hóa mã người tiêu dùng và thậm chí chạy nó trên phần cứng hoàn toàn khác nếu bạn muốn.
  2. MQ là một quy trình khá nổi tiếng và có rất nhiều dịch vụ thương mại. Điều này có nghĩa là bạn có thể dễ dàng viết hệ thống xử lý đơn đặt hàng của bạn trong C# bằng cách sử dụng MQ để di chuyển các tin nhắn trên, hoặc thậm chí sử dụng Spring Batch nếu bạn muốn. Heck, thậm chí còn có MQ Series cho máy chủ, vì vậy bạn có thể có xử lý đơn hàng của bạn xảy ra trên máy tính lớn trong COBOL nếu nó phù hợp với ưa thích của bạn.
  3. Thật ngu xuẩn là chỉ cần thêm nhiều người tiêu dùng hoặc nhà sản xuất hơn. Đơn giản chỉ cần đăng ký vào hàng đợi và đi họ đi!
  4. Tùy thuộc vào sản phẩm được sử dụng, Hàng đợi duy trì trạng thái sao cho các thông báo không bị "mất". Nếu tất cả người tiêu dùng chuyển sang ngoại tuyến, Hàng đợi sẽ chỉ sao lưu và lưu trữ tin nhắn cho đến khi người tiêu dùng quay lại.
  5. Hàng đợi cũng thường mạnh mẽ hơn. Các nhà sản xuất có thể đi xuống và người tiêu dùng bạn thậm chí không nao núng. Người tiêu dùng có thể đi xuống và nhà sản xuất thậm chí không cần biết.

Có một số nhược điểm. Bây giờ bạn có thêm một điểm thất bại.Bạn có thể sẽ muốn theo dõi độ sâu hàng đợi và sẽ cần cung cấp đủ dung lượng để lưu trữ thư khi bạn đang lưu trữ thư. Ngoài ra, nếu thời gian xử lý có thể là vấn đề, bạn có thể cần phải theo dõi cách xử lý nhanh các thứ nhanh trong hàng đợi để đảm bảo không sao lưu quá nhiều hoặc phá vỡ bất kỳ SLA nào có thể xảy ra.

Chỉnh sửa: Thêm dụ ... Nếu tôi có một lớp ren, ví dụ này:

public class MyWorkerThread implements Runnable { 
    private boolean run = true; 

    public void run() { 
    while (run) { 
     // Do work here... 
    } 

    // Do any thread cooldown procedures here, like stop listening to the Queue. 
    } 

    public void setRunning(boolean runState) { 
    run = runState; 
    } 
} 

Sau đó, tôi sẽ bắt đầu các chủ đề sử dụng một lớp học như thế này:

@Service("MyThreadManagerService") 
public class MyThreadManagerServiceImpl implements MyThreadManagerService { 
    private Thread[] workers; 
    private int workerPoolSize = 5; 

    /** 
    * This gets ran after any constructors and setters, but before anything else 
    */ 
    @PostConstruct 
    private void init() { 
    workers = new Thread[workerPoolSize]; 
    for (int i=0; i < workerPoolSize; i++) { 
     workers[i] = new Thread(new MyWorkerThread()); // however you build your worker threads 
     workers[i].start(); 
    } 
    } 

    /** 
    * This gets ran just before the class is destroyed. You could use this to 
    * shut down the threads 
    */ 
    @PreDestroy 
    public void dismantle() { 
    // Tell each worker to stop 
    for (Thread worker : workers) { 
     worker.setRunning(false); 
    } 

    // Now join with each thread to make sure we give them time to stop gracefully 
    for (Thread worker : workers) { 
     worker.join(); // May want to use the one that allows a millis for a timeout 
    } 

    } 

    /** 
    * Sets the size of the worker pool. 
    */ 
    public void setWorkerPoolSize(int newSize) { 
    workerPoolSize = newSize; 
    } 
} 

Bây giờ bạn có một lớp dịch vụ tốt đẹp, bạn có thể thêm các phương thức để theo dõi, khởi động lại, dừng, vv, tất cả các chuỗi công việc của bạn. Tôi đã làm cho nó một @Service bởi vì nó cảm thấy nhiều hơn một đơn giản @Component, nhưng về mặt kỹ thuật nó có thể là bất cứ điều gì miễn là mùa xuân biết để nhặt nó lên khi bạn đang autowiring. Phương thức init() trên lớp dịch vụ là những gì khởi động chủ đề và dismantle() được sử dụng để chặn chúng một cách duyên dáng và đợi chúng hoàn thành. Họ sử dụng các chú thích @PostConstruct@PreDestroy, vì vậy bạn có thể đặt tên cho chúng bất cứ điều gì bạn muốn. Bạn có thể có một hàm tạo trên MyWorkerThread để thiết lập Hàng đợi và như vậy. Ngoài ra, như là một tuyên bố từ chối trách nhiệm, tất cả điều này được viết từ bộ nhớ để có thể có một số vấn đề biên dịch nhẹ hoặc tên phương thức có thể hơi bị tắt.

Có thể có các lớp học đã sẵn sàng để làm việc này, nhưng tôi chưa bao giờ thấy một mình. Có ai đó biết cách tốt hơn bằng cách sử dụng các bộ phận có sẵn mà tôi muốn được đào tạo tốt hơn không.

+0

Cảm ơn bạn đã trả lời. Tôi đã xem xét JMS bằng ActiveMQ và làm việc xuất bản (tức là thêm các đơn hàng vào hàng đợi). Bây giờ tôi đang chuyển sang người tiêu dùng và đang sử dụng SimpleThreadPool, nhưng với ứng dụng của tôi là một ứng dụng web (không có điểm vào chính), làm cách nào để đảm bảo nhóm luồng này chạy khi khởi động? –

+0

Vâng, một hồ bơi thread là nhiều hơn cho các chủ đề mà chạy sau đó chấm dứt. Bạn có thể có chúng trong một lịch trình hoặc một cái gì đó. Tôi nghĩ rằng sẽ tốt hơn nếu bạn có dịch vụ của mình khởi động chủ đề của bạn hoặc viết một "người duy trì" chủ đề tùy chỉnh. Tôi sẽ cập nhật câu trả lời của tôi với một ví dụ. – CodeChimp

0

Nếu kích thước đơn đặt hàng của bạn có thể phát triển trong tương lai và Bạn muốn có giải pháp có thể mở rộng, tôi khuyên bạn nên đi với Spring-Batch framework. Tôi tìm thấy nó rất dễ dàng để tích hợp với mùa xuân-MVC và với cấu hình tối thiểu, bạn có thể dễ dàng đạt được một kiến ​​trúc xử lý song song rất mạnh mẽ. Sử dụng Spring-batch-partitioning. Hy vọng điều này sẽ giúp bạn !! Vui lòng hỏi nếu bạn cần trợ giúp về tích hợp với Spring MVC.