在控制器内部使用非线程安全的库

Using a library that is not thread-safe inside of a controller

我不确定线程​​在 play 中是如何工作的,据我所知,netty 使用单个线程,但不确定这如何转化为控制器操作的调用方式。

class SomeController extends Controller {

  val processor = new PegDownProcessor()  // 

  def index = Action { request => 

    val result = processor.doSomething()

    Ok("hello")
  }

}

pegdown 库表示实例化 PegDownProcessor 可能需要 100 毫秒,并建议在应用程序中使用单个引用。

Note that the first time you create a PegDownProcessor it can take up to a few hundred milliseconds to prepare the underlying parboiled parser instance. However, once the first processor has been built all further instantiations will be fast. Also, you can reuse an existing PegDownProcessor instance as often as you want, as long as you prevent concurrent accesses, since neither the PegDownProcessor nor the underlying parser is thread-safe.

https://github.com/sirthias/pegdown

它还说它不是线程安全的。

上面的用法是否设计正确,我将单个实例用作控制器内部的 val,并实际在控制器操作内部使用它?

请解释它是否正确,即线程安全,或者为什么不正确?

可以从多个线程调用播放动作。

我脑海中突然想到一个快速的解决方案: 您可以创建一个处理器池。该池将是线程安全的,并将包含给定数量的处理器(您可以动态分配处理器数量或根据您拥有的 CPU/RAM 分配处理器数量)。当一个请求进来时,池将它放入一个(先进先出)队列中(当然你应该使用线程安全的队列实现)。每个处理器都在自己的线程上运行,当一个处理器完成一项作业时,它会检查队列中是否有新作业。 pool的enqueue方法returns一个Future,在处理任务时解析。 Play 支持控制器方法的异步结果,因此这也可以很好地与 Play 一起播放。

一个类似的解决方案是使用 Akka 及其参与者池功能,该功能基本上以更通用的方式实现了上述方法。由于 actor 是单线程的,每个 actor 都有一个对处理器的引用,并且会像在单线程上一样简单地做同样的事情。 Akka 允许高级选项,例如定义调度方法,并且也非常适合 Play 堆栈。 Akka 本身几乎没有开销,您可以创建数千个 actors 而不会出现任何性能问题。