我来自Perl背景,正在使用Spring编写我的第一个Java MVC Web应用程序。
我的webapp允许用户通过调用第三方SOAP服务来提交应用程序同步处理的订单。该项目的下一阶段是允许用户提交批量订单(例如包含500行的CSV)并异步处理它们。这是我现有控制器的片段:
@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";
}
}
我计划创建一个新的@RequestMapping
来处理传入的CSV并修改OrderService
以便能够拆分CSV并将各个订单保存到数据库。
我的问题是:在MVC Spring应用程序中创建后台工作程序的最佳方法是什么?理想情况下,我将有5个线程处理这些订单,并且很可能来自队列。我已阅读有关@Async
或向Runnable
bean提交SimpleAsyncTaskExecutor
的信息,我不知道该走哪条路。一些例子对我有帮助。
答案 0 :(得分:1)
我认为Spring Batch太过分了,而不是你真正想要的东西。它更像是批量处理,比如将所有订单写入文件然后一次处理,而这似乎更像是异步处理,你只想拥有一个工作“队列”并按此方式处理它。
如果确实如此,我会考虑使用JMS使用pub / sub模型。有几个JMS提供程序,例如Apache ActiveMQ或Pivotal RabitMQ。从本质上讲,您的OrderService
会将CSV分解为工作单元,将它们推送到JMS队列,并且您将有多个使用者设置从队列中读取并执行工作任务。有很多方法可以配置它,但我只是创建一个类来保存您的工作线程并使线程数可配置。这里的另一个好处是:
但是有一些缺点。你现在有一个额外的失败点。您可能希望监视队列深度,并且在缓存消息时需要提供足够的空间来存储消息。此外,如果处理的时间可能是一个问题,您可能需要监视队列中处理事务的速度,以确保它没有过多备份或破坏可能存在的任何SLA。
编辑:添加示例... 如果我有一个线程类,例如:
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;
}
}
然后我会使用这样的类来启动线程:
@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;
}
}
现在你有一个很好的服务类,你可以添加方法来监视,重启,停止等所有工作线程。我把它设为@Service
,因为它感觉比简单@Component
更正确,但从技术上讲,只要Spring知道在你自动装配时就把它拿起来,它就可以是任何东西。服务类上的init()
方法启动线程,dismantle()
用于优雅地停止它们并等待它们完成。他们使用@PostConstruct
和@PreDestroy
注释,因此您可以根据需要为其命名。您可能在MyWorkerThread
上有一个构造函数来设置队列等。此外,作为免责声明,这些都是从内存中写的,因此可能会有一些温和的编译问题或方法名称可能稍微偏离。
可能有类可以做这类事情,但我自己从未见过。有人知道使用现成的部件更好的方式,我希望得到更好的教育。
答案 1 :(得分:0)
如果您的订单规模可能在未来增长,并且您想要一个可扩展的解决方案,我建议您使用Spring-Batch framework。我发现很容易与spring-mvc集成,并且通过最少的配置,您可以轻松实现非常强大的并行处理架构。使用Spring-batch-partitioning。希望这可以帮助您!如果您需要有关与Spring MVC集成的帮助,请随时询问。