Websocket実装に向けて非同期処理を勉強する(REST API)
REST APIだけで実装しているbotにWebsocket入れたーい。
そんな思いでまちゅけんさんの記事を勉強したメモです。元記事はこちら。
import asyncio import aiohttp async def main(): async with aiohttp.ClientSession() as session: async with session.get("https://api.bybit.com/v2/public/tickers?symbol=BTCUSD") as resp: data = await resp.text() print(data) asyncio.run(main())
5行目で非同期コンテキストマネージャーを用いて HTTPクライアントセッション [3] を作成しています
いきなり用語がわからない。非同期コンテキストマネージャーとはなんぞや?
調べてみると、withがコンテキストマネージャというらしい。それをasyncで使ってるので非同期コンテキストマネージャ。
調べてないが、ClientSessionをwithで開くと、with構文終了後、セッションが勝手に閉じるようになってると思われる。
6行目でまた非同期コンテキストマネージャーを用いて HTTPリクエスト(GET)を行いレスポンスを待機 ( HTTP通信 [4] )します ( HTTPヘッダ が取得されます)
書いてあることはわかるが、必要性が理解できない。
5行目で非同期コンテキストマネージャー使ってるので、セッションは閉じるようになってるはず。6行目でも非同期コンテキストマネージャーを使う必要があるのか?なにか閉じるものがあるのか?
7行目のawaitとはどう使い分けているのか?根底コネクタってなんだ?
なお、コルーチン関数で実装されているメソッドはawait文で実行する必要があるらしい。へー。
そう考えていると、次にもう一つ違う書き方が。
import asyncio import aiohttp async def main(): async with aiohttp.ClientSession() as session: r = await session.get("https://api.bybit.com/v2/public/tickers", params={"symbol": "BTCUSD"}) data = await r.json() print(data["result"]) asyncio.run(main())
やっぱasync withでgetする必要はないらしい。だよねー。
import asyncio import aiohttp import time async def fetch(url, params={}): r = await session.get(url, params=params) data = await r.json() return data async def main(): global session async with aiohttp.ClientSession() as session: stime = time.time() results = [] coro1 = fetch("https://api.bybit.com/v2/public/tickers", params={"symbol": "BTCUSD"}) coro2 = fetch("https://api.bybit.com/v2/public/tickers", params={"symbol": "ETHUSD"}) coro3 = fetch("https://api.bybit.com/v2/public/tickers", params={"symbol": "EOSUSD"}) coro4 = fetch("https://api.bybit.com/v2/public/tickers", params={"symbol": "XRPUSD"}) task1 = asyncio.create_task(coro1) task2 = asyncio.create_task(coro2) task3 = asyncio.create_task(coro3) task4 = asyncio.create_task(coro4) await task1 await task2 await task3 await task4 results.append(task1.result()) results.append(task2.result()) results.append(task3.result()) results.append(task4.result()) print(results) etime = time.time() print(f"Total {etime - stime:.4f} sec") asyncio.run(main())
前節との最も重要な違いは20-23行目でコルーチンをTask化し実行を イベントループ にスケジュールし、それを24-27行目で各Taskが終了するのを await文 で待機しているところです。
前節ではイベントループにスケジュールせずに直接await文で実行していました。ここで分かるのは await文は "実行する" というよりも、awaitを行った行で制御を離してイベントループに任せて "awaitしたオブジェクトが終了するまで待機する" という命令 であることです。
すごくよくわかった。
非同期処理の本質的なメリットは、1つ目のリクエストへのレスポンスが返ってくる前に、2つ目のリクエストを投げられること。
そのため重要なのは、イベントループにタスクを入れきったあと、awaitで終了するまで待機すること!
Websocketは記事を分けます。