Skip to content

[Python] 使用 httpx 取代 requests 進行異步請求

Last Updated on 2024-07-28 by Clay

在 Python 的程式開發中,我們經常使用 requests 模組進行 HTTP 請求;然而 requests 在真正串連前後端及不同服務時其同步請求方式可能成為瓶頸,我最近正是因為使用 requests 造成 K8s 的探針阻塞、進而導致所開發的服務容器被誤刪 —— 此時,httpx 可能是一個更適合的異步請求操作模組。


httpx 介紹

httpx 其實聽名字就知道,它是一個 HTTP 客戶端的函式庫,無論是同步(sync)還是異步(sync)的 API 調用都原生支援,可以直接兼容於 Python 的 asyncawait 關鍵字進行操作。

除此之外它也同樣具備一些強大的功能,比如支援 proxy、支援 SSL 設定、可以自訂重試策略(不用再自己寫了!)… 等等。以現在的開發環境來說,我個人體感它幾乎可以完全取代 requests 模組。

以下,我們來實際列舉 httpx 的使用方式,不過在那之前,我們可以建立一個假的 FastAPI 服務和原本的 requests 寫法。

當然,如果你已經有現成的程式碼想要修改,也可以直接跳到最下面 httpx 語法的部份,開始修改自己的程式碼。


建立 FastAPI 服務(假)

首先安裝 fastapiuvicorn

pip install fastapi uvicorn


之後,我們來實際建立一個服務。

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

app = FastAPI()

class Item(BaseModel):
    id: int
    name: str

fake_db: List[Item] = []

@app.get("/items/", response_model=List[Item]])
async def read_items():
    return fake_db

@app.post("/items/", response_model=Item])
async def create_item(item: Item):
    fake_db.append(item)
    return item


緊接著將其啟動:

uvicorn app:app --reload


Output:

INFO:     Started server process [560791]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

預設開啟在 http://127.0.0.1:8000 上。


原本 requests 的語法

如果你的系統還沒有安裝 requests但是都來看這篇筆記了應該很難沒有安裝吧),可以參考下面指令進行安裝:

pip install requests


之後,我們就可以使用 Python 撰寫程式去對 APIs 發起請求。

import requests

# GET
response = requests.get("http://127.0.0.1:8000/api/fake-db/items")
print("GET:", response.json())

# POST
new_item = {"id": 1, "name": "Item 1"}
response = requests.post("http://127.0.0.1:8000/api/fake-db/items", json=new_item)
print("POST:", response.json())

# GET
response = requests.get("http://127.0.0.1:8000/api/fake-db/items")
print("GET:", response.json())


Output:

GET: []
POST: {'id': 1, 'name': 'Item 1'}
GET: [{'id': 1, 'name': 'Item 1'}]

可以看到,一切都正常運作。

到了這裡,我們已經完成了 FastAPI 建立服務、再使用 requests 發送同步的請求;如果我們只是要兼容同步請求,我們可以在這個地方,把所有的 requests 文字全部替換成 httpx這樣就是 httpx 的同步請求方式、是完全兼容 requests 的語法,不錯吧?

httpx 優秀的地方在於異步請求,我們接下來就來看 httpx 該如何執行異步請求吧!


httpx 的異步請求方式

最簡單的 httpx 異步請求方式,需要使用到 httpx.AsyncClient() 這種類別。

import asyncio
import httpx


async def main():
    async with httpx.AsyncClient() as client:
        # GET
        response = await client.get("http://127.0.0.1:8000/api/fake-db/items")
        print("GET:", response.json())

        # POST
        new_item = {"id": 2, "name": "Item 2"}
        response = await client.post("http://127.0.0.1:8000/api/fake-db/items", json=new_item)
        print("POST:", response.json())

        # GET again
        response = await client.get("http://127.0.0.1:8000/api/fake-db/items")
        print("GET:", response.json())


# Run
asyncio.run(main())


Output:

GET: [{'id': 1, 'name': 'Item 1'}]
POST: {'id': 2, 'name': 'Item 2'}
GET: [{'id': 1, 'name': 'Item 1'}, {'id': 2, 'name': 'Item 2'}]

我們可以看到請求已經很好地被執行完成了。


結語

在本篇筆記中,我們介紹了如何把 requests 的同步請求方式修改成 httpx 的異步請求方式,進而利用異步編程的優勢提昇應用程式的性能和效率。

如果我們的服務會需要使用到大量的 HTTP 請求操作,那麼使用 httpx 是個再恰當不過的選擇。不過雖然 httpx 再大部份的情況下都可以取代 requests,還提供了更多的功能,但並不是 100% 完全相同,在某些極端情況下還是會有行為差異的。

所以如果應用程式出現了難以理解的錯誤,不能完全把這種可能性排除掉。祝福大家(包括我)寫的程式都能越來越精簡、效能越來越好、研究的項目也都會出現大幅度的突破!


References


Read More

Tags:

Leave a Reply