動かざることバグの如し

近づきたいよ 君の理想に

fastapiとHTTPXで非同期APIリクエストをやってみる

環境

やりたいこと

fastapiというPythonAPIフレームワークがある

fastapi.tiangolo.com

APIが外部の別のAPIを呼び出すことはよくある。fatsapiがネイティブで非同期(async)に対応していることもあり、 せっかくなのでHTTPリクエスト部分も非同期で書いてみたかった

コード

PythonでHTTPリクエストする場合はrequests使うのが一番多いと思うがasyncには対応してないのでHTTPXというライブラリを使う

pip install httpx

でインストール可能

例外処理も含めたサンプルコードが以下

import httpx
from fastapi import HTTPException, status, FastAPI

app = FastAPI()

async def request():
  async with httpx.AsyncClient() as client:
    response: httpx.Response = await client.get("https://httpbin.org/uuid")
    if response.status_code != httpx.codes.OK:
      raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE)
  return response.json()

@app.get('/httpx')
async def bench_httpx():
  return await request()

こんな感じに表示されるはず

{
  "uuid": "8716ba20-8443-4857-bf30-4fcd39e7a909"
}

せっかくなのでrequests版も作ってベンチマークとってみた

from fastapi import HTTPException, status, FastAPI
import requests

app = FastAPI()

@app.get('/requests')
def bench_requests():
  response = requests.get('https://httpbin.org/uuid')
  if response.status_code != requests.codes.ok:
    raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE)
  return response.json()

で結果 HTTPX版の方/

❯ hey http://localhost:3000/httpx -n 1000 -c 50 -t 2

Summary:
  Total:        4.8898 secs
  Slowest:      2.3744 secs
  Fastest:      0.7238 secs
  Average:      1.0300 secs
  Requests/sec: 40.9016
  
  Total data:   9400 bytes
  Size/request: 47 bytes

Response time histogram:
  0.724 [1]     |■
  0.889 [56]    |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  1.054 [78]    |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  1.219 [30]    |■■■■■■■■■■■■■■■
  1.384 [21]    |■■■■■■■■■■■
  1.549 [12]    |■■■■■■
  1.714 [1]     |■
  1.879 [0]     |
  2.044 [0]     |
  2.209 [0]     |
  2.374 [1]     |■


Latency distribution:
  10% in 0.8375 secs
  25% in 0.8842 secs
  50% in 0.9557 secs
  75% in 1.1458 secs
  90% in 1.2995 secs
  95% in 1.4624 secs
  99% in 1.5652 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0002 secs, 0.7238 secs, 2.3744 secs
  DNS-lookup:   0.0001 secs, 0.0000 secs, 0.0021 secs
  req write:    0.0001 secs, 0.0000 secs, 0.0006 secs
  resp wait:    1.0296 secs, 0.7236 secs, 2.3720 secs
  resp read:    0.0001 secs, 0.0000 secs, 0.0002 secs

Status code distribution:
  [200] 200 responses

以下はrequests版

❯ hey http://localhost:3000/requests -n 1000 -c 50 -t 2

Summary:
  Total:        5.5102 secs
  Slowest:      2.9772 secs
  Fastest:      0.6944 secs
  Average:      0.9763 secs
  Requests/sec: 36.2963
  
  Total data:   9400 bytes
  Size/request: 47 bytes

Response time histogram:
  0.694 [1]     |
  0.923 [129]   |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  1.151 [36]    |■■■■■■■■■■■
  1.379 [9]     |■■■
  1.608 [9]     |■■■
  1.836 [7]     |■■
  2.064 [1]     |
  2.292 [6]     |■■
  2.521 [0]     |
  2.749 [0]     |
  2.977 [2]     |■


Latency distribution:
  10% in 0.7313 secs
  25% in 0.7558 secs
  50% in 0.8162 secs
  75% in 1.0259 secs
  90% in 1.5373 secs
  95% in 1.8023 secs
  99% in 2.9076 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0007 secs, 0.6944 secs, 2.9772 secs
  DNS-lookup:   0.0004 secs, 0.0000 secs, 0.0019 secs
  req write:    0.0002 secs, 0.0000 secs, 0.0016 secs
  resp wait:    0.9752 secs, 0.6943 secs, 2.9770 secs
  resp read:    0.0001 secs, 0.0000 secs, 0.0012 secs

Status code distribution:
  [200] 200 responses

あれ、あんまり変わらない。。。? それどころかHTTPX版のほうが遅いときもある

参考リンク