gpt4 book ai didi

multithreading - Play Framework : What happens when requests exceeds the available threads

转载 作者:行者123 更新时间:2023-12-04 13:07:08 25 4
gpt4 key购买 nike

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

  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 个请求应该被拒绝(或排队直到超时?)。但是我观察到额外的工作线程被创建来服务其余的请求。我还观察到延迟增加(服务请求变慢),因为池中的线程数小于接收到的请求数。

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

最佳答案

您的测试结构不正确,无法测试您的假设。
如果你超过 this section in the docs你会看到 Play 有几个线程池/执行上下文。关于您的问题,重要的是 默认线程池 以及这与您的操作所服务的 HTTP 请求有何关系。

正如文档所述,默认线程池是默认运行所有应用程序代码的地方。 IE。所有操作代码,包括所有 Future的(没有明确定义自己的执行上下文),将在这个执行上下文/线程池中运行。所以使用你的例子:

def sync = Action {

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

Ok("Done")
}
// *** 未评论您操作中的所有代码将在默认线程池中运行。
IE。当请求被路由到您的操作时:
  • FutureThread.sleep将被分派(dispatch)到您的自定义执行上下文
  • 然后无需等待Future完成(因为它在自己的线程池 [ Context.blockingPool ] 中运行,因此不会阻塞默认线程池上的任何线程)
  • 您的 Ok("Done")语句被评估并且客户端收到响应
  • 大约。收到响应 100 毫秒后,您的 Future完成

  • 因此,为了解释您的观察,当您同时发送 100 个请求时,Play 将很乐意接受这些请求,路由到您的 Controller 操作(在默认线程池上执行),发送到您的 Future然后回复客户端。

    默认池的默认大小是
    play {
    akka {
    ...
    actor {
    default-dispatcher = {
    fork-join-executor {
    parallelism-factor = 1.0
    parallelism-max = 24
    }
    }
    }
    }
    }

    每个内核使用 1 个线程,最多 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 一个一个处理请求。这是你期望看到的......

    关于multithreading - Play Framework : What happens when requests exceeds the available threads,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24579030/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com