加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – Akka Http性能调优

发布时间:2020-12-16 09:11:46 所属栏目:安全 来源:网络整理
导读:我在Akka-http框架(版本:10.0)上执行负载测试,我正在使用 wrk工具. wrk命令: wrk -t6 -c10000 -d 60s –timeout 10s –latency http:// localhost:8080 / hello 首先运行没有任何阻塞调用, object WebServer { implicit val system = ActorSystem("my-sy
我在Akka-http框架(版本:10.0)上执行负载测试,我正在使用 wrk工具.
wrk命令:

wrk -t6 -c10000 -d 60s –timeout 10s –latency http:// localhost:8080 / hello

首先运行没有任何阻塞调用,

object WebServer {

  implicit val system = ActorSystem("my-system")
  implicit val materializer = ActorMaterializer()
  implicit val executionContext = system.dispatcher
  def main(args: Array[String]) {


    val bindingFuture = Http().bindAndHandle(router.route,"localhost",8080)

    println(
      s"Server online at http://localhost:8080/nPress RETURN to stop...")
    StdIn.readLine() // let it run until user presses return
    bindingFuture
      .flatMap(_.unbind()) // trigger unbinding from the port
      .onComplete(_ => system.terminate()) // and shutdown when done
  }
}

object router {
  implicit val executionContext = WebServer.executionContext


  val route =
    path("hello") {
      get {
        complete {
        "Ok"
        }
      }
    }
}

输出wrk:

Running 1m test @ http://localhost:8080/hello
  6 threads and 10000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.22ms   16.41ms   2.08s    98.30%
    Req/Sec     9.86k     6.31k   25.79k    62.56%
  Latency Distribution
     50%    3.14ms
     75%    3.50ms
     90%    4.19ms
     99%   31.08ms
  3477084 requests in 1.00m,477.50MB read
  Socket errors: connect 9751,read 344,write 0,timeout 0
Requests/sec:  57860.04
Transfer/sec:      7.95MB

现在如果我在路由中添加未来呼叫并再次运行测试.

val route =
    path("hello") {
      get {
        complete {
          Future { // Blocking code
            Thread.sleep(100)
            "OK"
          }
        }
      }
    }

输出:wrk:

Running 1m test @ http://localhost:8080/hello
  6 threads and 10000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   527.07ms  491.20ms  10.00s    88.19%
    Req/Sec    49.75     39.55   257.00     69.77%
  Latency Distribution
     50%  379.28ms
     75%  632.98ms
     90%    1.08s 
     99%    2.07s 
  13744 requests in 1.00m,1.89MB read
  Socket errors: connect 9751,read 385,write 38,timeout 98
Requests/sec:    228.88
Transfer/sec:     32.19KB

正如你可以看到,将来只有13744个请求被提供.

在Akka documentation之后,我为路由添加了一个单独的调度程序线程池,创建了最多200个线程.

implicit val executionContext = WebServer.system.dispatchers.lookup("my-blocking-dispatcher")
// config of dispatcher
my-blocking-dispatcher {
  type = Dispatcher
  executor = "thread-pool-executor"
  thread-pool-executor {
    // or in Akka 2.4.2+
    fixed-pool-size = 200
  }
  throughput = 1
}

经过上述变化,性能有所提升

Running 1m test @ http://localhost:8080/hello
  6 threads and 10000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   127.03ms   21.10ms 504.28ms   84.30%
    Req/Sec   320.89    175.58   646.00     60.01%
  Latency Distribution
     50%  122.85ms
     75%  135.16ms
     90%  147.21ms
     99%  190.03ms
  114378 requests in 1.00m,15.71MB read
  Socket errors: connect 9751,read 284,timeout 0
Requests/sec:   1903.01
Transfer/sec:    267.61KB

在my-blocking-dispatcher配置中,如果我将池大小增加到200以上,那么性能是相同的.

现在,我应该使用什么其他参数或配置来增加使用将来的调用时的性能.因此该应用程序可以提供最大的吞吐量.

解决方法

一些免责声明:我以前没有使用过wrk工具,所以我可能会出错.这是我为这个答案做出的假设:

>连接计数与线程计数无关,即如果我指定-t4 -c10000,则保持10000个连接,而不是4 * 10000.
>对于每个连接,行为如下:它发送请求,完全接收响应,并立即发送下一个等等,直到时间用完.

此外,我已经在与机器相同的机器上运行服务器,我的机器似乎比您的机器弱(我只有双核CPU),所以我将wrk的线程数减少到2,连接数为1000,得到体面的结果.

我使用的Akka Http版本是10.0.1,wrk版本是4.0.2.

现在回答我们来看看你拥有的阻止代码:

Future { // Blocking code
  Thread.sleep(100)
  "OK"
}

这意味着每个请求至少需要100毫秒.如果您有200个线程和1000个连接,则时间线将如下所示:

Msg: 0       200      400      600      800     1000     1200      2000
     |--------|--------|--------|--------|--------|--------|---..---|---...
Ms:  0       100      200      300      400      500      600      1000

Msg是处理消息的数量,Ms是以毫秒为单位的时间.

这给我们每秒处理的2000条消息,或者每30秒处理60000条消息,这些消息大多同意测试数据:

wrk -t2 -c1000 -d 30s --timeout 10s --latency http://localhost:8080/hello
Running 30s test @ http://localhost:8080/hello
  2 threads and 1000 connections
  Thread Stats   Avg     Stdev     Max   +/- Stdev
    Latency   412.30ms   126.87ms 631.78ms   82.89%
    Req/Sec     0.95k    204.41     1.40k    75.73%
  Latency Distribution
     50%  455.18ms
     75%  512.93ms
     90%  517.72ms
     99%  528.19ms
here: --> 56104 requests in 30.09s <--,7.70MB read
  Socket errors: connect 0,read 1349,write 14,timeout 0
Requests/sec:   1864.76
Transfer/sec:    262.23KB

这也是很明显的,这个数字(每秒2000个消息)被线程计数严格约束.例如.如果我们有300个线程,我们将每100毫秒处理300条消息,所以如果我们的系统可以处理这么多线程,我们每秒会有3000条消息.让我们看看如果我们提供每个连接1个线程,即池中的1000个线程,我们将如何收费:

wrk -t2 -c1000 -d 30s --timeout 10s --latency http://localhost:8080/hello
Running 30s test @ http://localhost:8080/hello
  2 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   107.08ms   16.86ms 582.44ms   97.24%
    Req/Sec     3.80k     1.22k    5.05k    79.28%
  Latency Distribution
     50%  104.77ms
     75%  106.74ms
     90%  110.01ms
     99%  155.24ms
  223751 requests in 30.08s,30.73MB read
  Socket errors: connect 0,read 1149,write 1,timeout 0
Requests/sec:   7439.64
Transfer/sec:      1.02MB

正如你所看到的,现在一个请求平均几乎完全是100ms,也就是说我们把它放在Thread.sleep中的数量相同.看来我们不能比这快得多!现在,我们几乎在每个请求的一个线程的标准情况下,多年来一直工作得很好,直到异步IO让服务器扩展得更高.

为了比较起见,这里是我的机器上使用默认fork-join线程池的完全非阻塞测试结果:

complete {
  Future { // Blocking code
    Thread.sleep(100)
    "OK"
  }
}

====>

wrk -t2 -c1000 -d 30s --timeout 10s --latency http://localhost:8080/hello
Running 30s test @ http://localhost:8080/hello
  2 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    15.50ms   14.35ms 468.11ms   93.43%
    Req/Sec    22.00k     5.99k   34.67k    72.95%
  Latency Distribution
     50%   13.16ms
     75%   18.77ms
     90%   25.72ms
     99%   66.65ms
  1289402 requests in 30.02s,177.07MB read
  Socket errors: connect 0,read 1103,write 42,timeout 0
Requests/sec:  42946.15
Transfer/sec:      5.90MB

总而言之,如果使用阻塞操作,则每个请求需要一个线程来实现最佳吞吐量,因此相应地配置您的线程池.系统可以处理多少线程有自然限制,您可能需要调整操作系统以获得最大线程数.为了获得最佳吞吐量,请避免阻塞操作.

也不要将异步操作与非阻塞操作混淆.您的代码与Future和Thread.sleep是异步但阻止操作的完美示例.许多流行的软件以这种模式运行(一些传统的HTTP客户端,Cassandra驱动程序,AWS Java SDK等).为了充分获得非阻塞HTTP服务器的优点,您需要一直阻止一切,而不仅仅是异步.这可能不总是可能的,但这是值得的.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读