Spring MVC后台进程

smi*_*tan 6 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";
    }
}
Run Code Online (Sandbox Code Playgroud)

我计划创建一个新的@RequestMapping来处理传入的CSV并修改它OrderService以便能够将CSV分开并将各个订单持久保存到数据库中.

我的问题是:在MVC Spring应用程序中创建后台工作程序的最佳方法是什么?理想情况下,我将有5个线程处理这些订单,并且很可能来自队列.我已经读过@Async或提交过Runnable一个SimpleAsyncTaskExecutorbean,我不知道该走哪条路.一些例子对我有帮助.

Cod*_*imp 1

我认为 Spring Batch 太过分了,并不是您真正想要的。它更多的是用于批处理,例如将所有订单写入文件,然后立即处理所有订单,而这似乎更像是异步处理,您只想拥有一个工作“队列”并以这种方式处理它。

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

  1. 您可以将消费者代码外部化,如果您愿意,甚至可以让它在完全不同的硬件上运行。
  2. MQ 是一个非常知名的流程,并且有很多商业产品。这意味着您可以使用 MQ 轻松地用 C# 编写订单处理系统来移动消息,如果您愿意,甚至可以使用 Spring Batch。哎呀,甚至还有用于主机的 MQ 系列,因此如果您喜欢的话,您可以在 COBOL 的大型机上进行订单处理。
  3. 简单地添加更多消费者或生产者是愚蠢的。只需订阅队列,他们就可以走了!
  4. 根据所使用的产品,队列会维护状态,因此消息不会“丢失”。如果所有消费者都离线了,队列将简单地备份和存储消息,直到消费者回来。
  5. 队列通常也更加稳健。生产者可能会倒下,而消费者甚至不会退缩。消费者可以下降,生产者甚至不需要知道。

但也有一些缺点。您现在有一个额外的故障点。您可能需要监视队列深度,并且在缓存消息时需要提供足够的空间来存储消息。此外,如果处理时间可能是一个问题,您可能需要监控队列中的处理速度,以确保它不会备份太多或破坏任何可能存在的 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我将使用这样的类启动线程:

@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;
  }
}
Run Code Online (Sandbox Code Playgroud)

现在您有了一个很好的服务类,您可以添加方法来监视、重新启动、停止等所有工作线程。我将其设为 是@Service因为它感觉比简单的更正确@Component,但从技术上讲,它可以是任何东西,只要 Spring 知道在自动装配时选择它即可。服务类上的方法init()用于启动线程,并dismantle()用于优雅地停止它们并等待它们完成。它们使用@PostConstruct@PreDestroy注释,因此您可以随意命名它们。您可能会有一个构造函数MyWorkerThread来设置队列等。另外,作为免责声明,这都是从内存中编写的,因此可能存在一些轻微的编译问题或方法名称可能略有偏差。

可能已经有课程可以做这类事情,但我自己从未见过。有人知道使用现成零件的更好方法吗,我很乐意接受更好的教育。