Elixirを使ってAPIの負荷試験ツールを作りましたな話です。
Table of Contents
負荷試験について本気出して考えてみた
業務のなかで、負荷試験について考える機会が多かった年始明けでしたので、せっかくなのでよりいい負荷試験ツールを自作してみようかと思いました。
負荷といえば並列なリクエスト、並列といえばやっぱジャイアンツElixirということで、Elixirで実装することにしました。
ElixirでHttpリクエストをするならHttpoison
HTTPoisonはElixirで使えるHTTPクライアントです。
使い方もとっても簡単で、GET、POSTは下記のように実施できます。
# GET
HTTPoison.get!("http://example.com",[],[{:timeout, :infinity}, {:recv_timeout, :infinity}])
# POST
payload = Poison.encode!(%{"contentType" => "image/png", "key" => "value"})
ret = HTTPoison.post!("http://example.com", payload, [],[{:timeout, :infinity}, {:recv_timeout, :infinity}])
upload_id = Poison.decode!(ret.body)["upload_id"]
GETはHTTPoison.get()、POSTはHTTPoison.post!() で実施できます。簡単ですね。
! がついているかついていないかは例外を上げるか上げないかの違いです。
ナオキ「例外あ、あげますね・・・。」
1つ目の引数はURLですね。
Payloadは2つ目の引数。Posion.encode!() にMapでKVを入れれば実現できます。これも簡単ですね!!残念ながら、GETのbodyは許容しません。
3つ目はHeader。今回は使いませんね。
4つ目はOptions。Timeoutなどを設定します。
Timeoutは**:recv_timeoutと:timeout**の2つを指定します。 負荷試験なので、Infinityにしておきます。
ResponseがJsonの場合は**Poison.decode!()**でKVでアクセスできます。簡単ですね!!
Elixirで並列処理するならTask.asyncでしょ
正直ここらへんはElixirのHttpoisonでAPI負荷検証ツールをつくったを参考にしてます。
def send_requests_parallel(process_num,count) do
time_total = Enum.map(1..process_num, &Task.async(fn ->
&1
send_requests(@url)
end))
|> Enum.map(fn(task) -> Task.await(task,1000_000) end)
|> Enum.reduce(0, fn x,total -> total + x end)
IO.inspect "#{count}, average_time: #{time_total / process_num / 1000} ms, time_total: #{time_total / 1000} ms"
end
とやることで、並列処理&返ってきた実行時間をreduceで集計できます。すごいすごい。
CSVに出力
こちらも超簡単。CSVという便利なライブラリをmixから落とせば簡単に実現できます。
def write_csv() do
file = File.open!("response.csv", [:append, :utf8])
table_data = [["aaa","bbb", "ccc", "ddd"]]
table_data |> CSV.encode(headers: false) |> Enum.each(&IO.write(file, &1))
end
簡単ですね!
完成
完成しました。
ついでにElixirのドキュメントツールex_docを使ってドキュメントも作ってみました。
Ebook Homebrew: ElixirPerformanceTool
Elixirとっつきにくいかなぁと思いましたが、意外に簡単でした。
こんな感じでAPIのレスポンスタイムを取得できました。
iex> ElixirPerformanceTool.run(10,10)
"0, average_time: 1422.0 ms, time_total: 1422.0 ms"
"2, average_time: 843.0 ms, time_total: 843.0 ms"
"1, average_time: 1969.0 ms, time_total: 1969.0 ms"
"3, average_time: 844.0 ms, time_total: 844.0 ms"
"4, average_time: 844.0 ms, time_total: 844.0 ms"