Play框架:当请求超出可用线程时会发生什么

资料使用者

我在线程池服务阻止请求中有一个线程。

  def sync = Action {
    import Contexts.blockingPool
    Future { 
        Thread.sleep(100)
    } 
    Ok("Done")
  }

在Contexts.blockingPool中配置为:

custom-pool {
    fork-join-executor {
            parallelism-min = 1
            parallelism-max = 1
    }
}

从理论上讲,如果上述请求同时接收到100个请求,则预期的行为应该是:1个请求应该休眠(100),其余99个请求应该被拒绝(或排队直到超时?)。但是,我观察到创建了额外的工作线程来处理其余的请求。我还观察到,当池中的线程数小于接收到的请求时,延迟随着(变得慢于服务请求)而增加。

如果收到大于配置的线程池大小的请求,预期的行为是什么?

多诺万·穆勒(Donovan Muller)

您的检验结构不正确,无法检验您的假设。如果您在文档中仔细阅读本节,将会看到Play具有一些线程池/执行上下文。关于您的问题,最重要的一个是默认线程池以及它与您的操作所服务的HTTP请求之间的关系。

如文档所述,默认情况下,默认线程池是所有应用程序代码运行的地方。即,所有动作代码,包括所有动作代码Future(未明确定义其自己的执行上下文),都将在此执行上下文/线程池中运行。因此,使用您的示例:

def sync = Action {

  // *** import Contexts.blockingPool
  // *** Future { 
  // *** Thread.sleep(100)
  // ***} 

  Ok("Done")
}

您操作中所有注释的代码// ***将在默认线程池中运行即当请求路由到您的操作时:

  1. Future一起Thread.sleep将分派到您的自定义执行上下文
  2. 然后不用等待它Future完成(因为它在自己的线程池[ Context.blockingPool]中运行,因此不会阻塞默认线程池上的任何线程)
  3. 您的Ok("Done")陈述被评估,客户收到回复
  4. 大约 收到响应后100毫秒,您便Future完成了

因此,为了解释您的观察,当您发送100个并发请求时,Play会很乐意接受这些请求,路由到您的控制器操作(在默认线程池上执行),分派给您Future,然后响应客户端。

默认池的默认大小为

play {
  akka {
    ...
    actor {
      default-dispatcher = {
        fork-join-executor {
          parallelism-factor = 1.0
          parallelism-max = 24
        }
      }
    }
  }
}

每个内核最多使用24个线程。每个线程最多使用24个线程。考虑到您的操作几乎没有(不包括Future),您将能够处理每秒1000个请求。Future会通过但积压工作更长的时间花费,因为你是在自定义池阻塞线程只有(blockingPool)。

如果您使用我的操作略有调整的版本,您将在日志输出中看到确认上述说明的内容:

object Threading {

  def sync = Action {
    val defaultThreadPool = Thread.currentThread().getName;

    import Contexts.blockingPool
    Future {
      val blockingPool = Thread.currentThread().getName;
      Logger.debug(s"""\t>>> Done on thread: $blockingPool""")
      Thread.sleep(100)
    }

    Logger.debug(s"""Done on thread: $defaultThreadPool""")
    Results.Ok
  }
}

object Contexts {
  implicit val blockingPool: ExecutionContext = Akka.system.dispatchers.lookup("blocking-pool-context")
}

您的所有请求都将被快速处理,然后再Future一个一个地完成。

因此,总而言之,如果您真的想测试Play如何仅用一个线程处理请求来处理许多并发请求,那么可以使用以下配置:

play {
  akka {
    akka.loggers = ["akka.event.Logging$DefaultLogger", "akka.event.slf4j.Slf4jLogger"]
    loglevel = WARNING
    actor {
      default-dispatcher = {
        fork-join-executor {
          parallelism-min = 1
          parallelism-max = 1
        }
      }
    }
  }
}

您可能还想向操作中添加一个Thread.sleep(这样可以降低默认线程池中的寂寞线程的速度)

    ...
    Thread.sleep(100)
    Logger.debug(s"""<<< Done on thread: $defaultThreadPool""")
    Results.Ok
}

现在,您将拥有1个用于请求的线程和1个用于您Future线程如果您以高并发连接数运行此程序,您会注意到客户端在Play一步一步地处理请求时阻塞。您期望看到的是...

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

线程分叉时会发生什么?

当整数超出最大值时会发生什么?

当两个线程同时调用相同的静态方法时会发生什么?

Java线程设置为null时会发生什么?

线程引发异常时会发生什么?

当线程在等待wait()时被中断时会发生什么?

Java:从同步块启动新线程时会发生什么?

在Singleton Pattern中,当两个或多个线程同时执行时会发生什么?

当两个线程尝试修改/访问HashMap中的相同密钥时会发生什么?

完全使用WebContainer线程池(WebSphere)并收到新请求时会发生什么?

当线程进入Java中的同步块/方法时会发生什么情况

当子线程崩溃并且主线程等待加入时会发生什么?

当您丢失对Python线程的所有引用时会发生什么?

当线程“唤醒”某物时会发生什么

当我在lambda函数中启动新线程并返回时会发生什么

当两个或多个线程或进程ftruncate(2)相同文件时会发生什么?

Python,线程和请求:当我在一个会话中发出并发请求时会发生什么?

在同一react块中使用不同的线程调度程序时会发生什么?

当线程在C中调用longjmp()时会发生什么

Axios发出发布请求时会发生什么?

QFuture超出范围时会发生什么?

在浏览器中请求URL时会发生什么?

Microsoft SQL Server连接超出带宽时会发生什么?

多线程程序创建新进程时会发生什么?

暂停时会发生什么?

在 Swift 中,当两个线程获取并设置非线程安全属性时会发生什么?

关于实体框架,运行时会发生什么

c++ 当unique_ptr 的别名超出范围时会发生什么?

构造线程时会发生什么,以及线程是如何执行的