Spring MVC后台进程

时间:2014-07-21 09:49:15

标签: java spring-mvc background-process

我来自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的信息,我不知道该走哪条路。一些例子对我有帮助。

2 个答案:

答案 0 :(得分:1)

我认为Spring Batch太过分了,而不是你真正想要的东西。它更像是批量处理,比如将所有订单写入文件然后一次处理,而这似乎更像是异步处理,你只想拥有一个工作“队列”并按此方式处理它。

如果确实如此,我会考虑使用JMS使用pub / sub模型。有几个JMS提供程序,例如Apache ActiveMQ或Pivotal RabitMQ。从本质上讲,您的OrderService会将CSV分解为工作单元,将它们推送到JMS队列,并且您将有多个使用者设置从队列中读取并执行工作任务。有很多方法可以配置它,但我只是创建一个类来保存您的工作线程并使线程数可配置。这里的另一个好处是:

  1. 您可以外化消费者代码,如果愿意,甚至可以在完全不同的硬件上运行。
  2. MQ是一个非常着名的流程,并且有很多商业产品。这意味着您可以使用MQ轻松地在C#中编写订单处理系统来移动消息,甚至可以根据需要使用Spring Batch。哎呀,甚至有MQ系列主机,所以你可以在COBOL的大型机上进行订单处理,如果它适合你的想象。
  3. 简单地添加更多消费者或生产者是愚蠢的。只需订阅队列即可离开!
  4. 根据使用的产品,Queue维护状态,因此消息不会“丢失”。如果所有消费者都离线,队列将只是备份并存储消息,直到消费者回来。
  5. 队列通常也更强大。制片人可能会失败,消费者甚至不会退缩。消费者可能会失望,生产者甚至不需要知道。
  6. 但是有一些缺点。你现在有一个额外的失败点。您可能希望监视队列深度,并且在缓存消息时需要提供足够的空间来存储消息。此外,如果处理的时间可能是一个问题,您可能需要监视队列中处理事务的速度,以确保它没有过多备份或破坏可能存在的任何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集成的帮助,请随时询问。