Screaming Loud

日々是精進

spray-canで何故か同時に接続できずconnection reset by peerが発生する

sprayフレームワークを使っていると、HTTPリクエストの部分はspray-canを利用すると思います。

しばらく、spray-canを使っていたのですが、いくつか重い処理を走らせるとCPUを使い切っていないのに、sprayがリクエストを受け付けなくなってしまい、502 BadGateway を出すようになってしまいました。

そこで色々試したのですが、結果として設定のconfをいじるのではなく、responseを非同期にすればできました。

発生した現象

負荷をかけると以下のような connection reset by peerという502エラーが発生しました。

read: connection reset by peer

パラメータ変更

application.confを色々いじってみましたが、結局実装が間違っていたため、根本的なパフォーマンスは変わりませんでした。

spray/application.conf at master · spray/spray · GitHub
が、本家のベンチマークに使っているconf設定です。

sprayのconfは、

  • reference.conf:デフォルトでsprayに読み込まれているconf。いじれません。
  • application.conf:TypesafeConfigを使っていると自動で起動時に読み込まれるconf。起動時に -Dconfig.file=hoge で変更可能。includeを使うと、他の別confファイルも呼び出せます。

以下のようにいじりました。
spray-stress/application.conf at master · moc-yuto/spray-stress · GitHub


が計測してもパフォーマンスは変わらず。。。

解決策

FutureDirectiveを使うことでした。
使い方は、
spray | Documentation » 1.2.2 / 1.3.2 » spray-routing » Predefined Directives (by trait) » List of predefined directives by trait » FuturesDirectives » onComplete
です。

レスポンスにFutureを組み込むことで、502エラーが発生していたものが一切発生しなくなり、パフォーマンスを一気にあげることができました。

負荷実施

負荷にはvegetaを使用しました。
https://github.com/tsenart/vegeta

vegeta attack -rate=100 -duration=30s -targets=st.txt > st.bin

#st.txt
GET http://localhost:8080/st

Get http://localhost:8080/st に対する結果

Requests      [total, rate]            3000, 100.03
Duration      [total, attack, wait]    44.486880886s, 29.989999801s, 14.496881085s
Latencies     [mean, 50, 95, 99, max]  7.155629141s, 4.389581282s, 22.700462005s, 30.001109336s, 30.003361727s
Bytes In      [total, mean]            0, 0.00
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  0.00%
Status Codes  [code:count]             0:3000
Error Set:
Get http://localhost:8080/st: dial tcp 0.0.0.0:0->[::1]:8080: getsockopt: connection refused
Get http://localhost:8080/st: dial tcp 0.0.0.0:0->[::1]:8080: i/o timeout

Get http://localhost:8080/st2 に対する結果

Requests      [total, rate]            3000, 100.03
Duration      [total, attack, wait]    43.215084995s, 29.989999893s, 13.22508510
2s
Latencies     [mean, 50, 95, 99, max]  6.767752449s, 2.182035276s, 30.002461808s
, 30.005316637s, 30.074779909s
Bytes In      [total, mean]            30691, 10.23
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  31.10%
Status Codes  [code:count]             200:933  0:2067
Error Set:
Get http://localhost:8080/st2: dial tcp 0.0.0.0:0->[::1]:8080: getsockopt: connection refused
Get http://localhost:8080/st2: EOF
Get http://localhost:8080/st2: read tcp 127.0.0.1:54219->127.0.0.1:8080: read: c
onnection reset by peer
Get http://localhost:8080/st2: read tcp 127.0.0.1:54217->127.0.0.1:8080: read: c
onnection reset by peer

以上のような結果になりました。
さらにパラメータをチューニングした値は、また別途検証したいと思います。

負荷を受けさせたソースはこちら
github.com