Help us understand the problem. What is going on with this article?

ElixirのHttpoisonでAPI負荷検証ツールをつくった

More than 3 years have passed since last update.

次世代Web言語として名高く並列処理が得意なElixir、そしてElixirのHTTPクライアントライブラリのHTTPoisonを使って、開発しているAPIの負荷検証ツールを作ってみました。

なお、作成にあたって、こちらの記事を大変参考にさせていただきました。
ElixirのHTTPoisonライブラリの使い方
Phoenix で並列処理を使ったメタサーチ API を作ってみる

要件はこんな感じです。
・テスト用に作成したAPIを並列で叩きまくる
・秒間リクエスト数を指定できるようにしたい
・処理継続時間を指定できるようにしたい

早速やってみましょう。(Elixirはインストール済みのものとします)

プロジェクトの作成

プロジェクトの作成にはmixコマンドを使用します。

$ mix new performance_tool

すると、こんな感じのディレクトリ構成が自動で出来上がります。mix便利!

.
-- performance_tool
    |-- config
    |-- config.exs
    |-- lib
      `-- performance_tool.ex
    |-- mix.exs
    |-- README.md
    `- test
      |-- performance_tool_test.exs
      `-- test_helper.exs

依存関係の整理のため、mix.exsを編集

HTTPoisonを使用するため、./performance_tool/mix.exsを編集します。

defmodule PerformanceTest.Mixfile do
  ...
  def application do
    [applications: [:logger, :httpoison]]
  end

  def deps do
    [
      { :httpoison, "~> 0.7.4"}
    ]
  end
end

applicationのところに:httpoisonと追記しておくことで、実際のコードでHTTPoison.startの記述を省略できます。

実装

実際のコードは./performance_tool/lib/performance_tool.exに記述していきます。
ちなみにずっとJava一本で生きてきた私は「returnがないけどどうやって戻り値返すんだ?」と戸惑いましたが、どうやら関数の最終行の結果がそのまま関数の戻り値になるんだそうです。
また、Elixirでは実行結果を次に呼ぶ関数の第一引数に入れてくれるパイプ演算子(|>)というものが用意されているのでそれも使ってみます。

defmodule PerformanceTest do
  def run(process_num,seconds) do
    run(process_num,seconds,0)
  end

  #ここを再帰的に呼び出している
  def run(process_num,max_count,count) do 
    Task.async(fn -> send_requests_parallel(process_num,count) end)
    :timer.sleep(1000)
    run(process_num,max_count,count+1)
  end

  #run呼び出し時、max_countとcountが一致してた時だけこっちが動く
  def run(process_num,max_count,count) when max_count == count do 
    IO.puts("process end. count:#{count}")
  end

  def send_requests_parallel(process_num,count) do
    time_total = Enum.map(1..process_num, &Task.async(fn -> #関数の中身をprocess_numの回数分だけ非同期実行(戻り値=実行するtask)
      &1
      send_request("http://検証するAPIのURL")
    end))
    |> Enum.map(fn(task) -> Task.await(task,1000_000) end) #Task.asyncの内容が全部終わるまで待つ(戻り値=渡したtaskの実行結果)
    |> Enum.reduce(0, fn x,total -> total + x end) #実行時間を合計(ここの結果がtime_totalに格納される)

    #結果出力
    IO.inspect "#{count}, averave_time: #{time_total / process_num / 1000} ms, time_total: #{time_total / 1000} ms" 
  end

  def send_request(url) do
    #APIを叩いてかかった時間を返却する(今回はレスポンスは不要のためそのまま捨てているが、2つ目の戻り値にレスポンスが入っている)
    {time, _} = :timer.tc(fn -> HTTPoison.get!(url,[],[{:timeout, 10000000}]) end) 
    time
  end
end

実行

では、実際に動かしてみましょう。
performance_toolに記述したrun関数に1秒あたりの並行処理数と実行秒数を渡して実行します。

$ cd performance_tool
$ iex -S mix
iex(1)> PerformanceTest.run(10,10)
"0, averave_time: 98.9 ms, time_total: 989.0 ms"
"1, averave_time: 70.3 ms, time_total: 703.0 ms"
"2, averave_time: 64.3 ms, time_total: 643.0 ms"
"3, averave_time: 65.9 ms, time_total: 659.0 ms"
"4, averave_time: 62.8 ms, time_total: 628.0 ms"
"5, averave_time: 50.0 ms, time_total: 500.0 ms"
"6, averave_time: 62.3 ms, time_total: 623.0 ms"
"7, averave_time: 51.6 ms, time_total: 516.0 ms"
"8, averave_time: 120.2 ms, time_total: 1202.0 ms"
"9, averave_time: 50.0 ms, time_total: 500.0 ms"
process end. count:10
:ok

まとめ

・Elixirの関数では最後の行の結果が戻り値として返る
・Task.asyncで新しいプロセスを作成して非同期実行
・Task.awaitでTask.asyncで作られたプロセスの終了を待つ
・パイプ演算子(|>)は便利で楽しい

upsider
BtoBのFintechサービスを開発しているスタートアップ。
https://up-sider.com/lp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした