asyncioとは何か?
Scrapeless Proxiesは、あなた自身のHTTPクライアントからのリクエストを、設定可能なプロキシ接続経由で中継します。
asyncioは、非同期関数とイベントループを用いて並行処理を調整するための、Python標準ライブラリのフレームワークです。これは、プログラムがネットワーク応答、サブプロセス、またはその他の非同期インターフェースを持つ操作を待機する時間が長い場合に有用です。待機中のタスクはサスペンドして、他の実行可能なタスクが処理を続けられるようにします。
収集ジョブの例は、その違いをよく示します。ページの取得にはリモートサーバーを待つ時間が含まれますが、大きなドキュメントの解析にはローカル命令の実行が含まれます。asyncioは、前者の活動を独立した複数のリクエスト間でオーバーラップさせることができますが、後者の活動を自動的に複数のCPUコアにまたがって実行させることはできません。その境界を理解することで、非同期設計がアプリケーションの改善につながるかどうかを判断できます。
asyncioは実際には何を管理しているのか?
asyncioは非同期処理のスケジューリングとライフタイムを管理し、一方で別のクライアントライブラリが通常はアプリケーションプロトコルを実装します。 Python 非同期 I/O インターフェース には、タスク、同期処理、ネットワーク操作、およびサブプロセス管理のための機能が含まれます。HTTPクライアントはこれらの機能を利用して、ブロッキング読み取りでイベントループスレッドを占有することなく、応答を待機できます。
非同期関数はasync defで宣言します。これを呼び出すとコルーチンオブジェクトが生成されますが、呼び出しただけでは関数本体の実行は完了しません。コルーチンはawaitされるか、スケジュールされなければなりません。このため、関数名や宣言にasyncを付けただけでは、既存の同期コードが並行処理になるわけではありません。
実用的なプログラムでは3つの概念が繰り返し登場します。コルーチンはサスペンド可能な処理を表現します。タスクはコルーチンをスケジュールし、その最終的な結果を追跡します。イベントループは、I/Oの準備状態に応じて、実行可能なコールバックとタスクを調整します。Futureは最終的な結果を表し、しばしばより低レベルなAPIの内部に現れますが、アプリケーションコードが扱うのは通常コルーチンとタスクです。
イベントループはどのように実行を共有するのか?
イベントループは、そのスレッド内で協調的に処理を実行します。実行中のタスクは、完了するか、待機を要する操作でサスペンドするまで制御を保持します。ネットワークデータが利用可能になると、ループは関連するタスクを再開できます。このスケジューリングモデルは、非同期コードの各行が同時に実行されると仮定するやり方とは異なります。
awaitキーワードは、await可能な結果への依存関係を表現します。これは無条件に一時停止を指示するものではありません。awaitされた操作が即座に完了できる場合、別のタスクに実行が回らずに処理が継続されることがあります。そのため、サスペンドポイントの間に多くの計算を行うループは、関数が非同期であっても、無関係な処理を遅らせてしまう可能性があります。
小さなカタログ収集プログラムを考えてみましょう。あるタスクは商品ページを待ち、別のタスクはカテゴリーページを待ち、ライターは検証済みレコードを待っています。それぞれのタスクは、自身の依存対象が準備できたときに進行できます。もし商品パーサーがイベントループスレッド上で長い同期計算を行うと、その計算中はカテゴリタスクを再開できません。ネットワーク側ではすでに応答が届いていても、アプリケーションがそれを処理する順番が回ってくるまで待たなければならないのです。
逐次的なawaitと並行タスク
独立した処理を1つずつawaitすると、それらがすでにスケジュールされていない限り逐次実行のままになります。もし関数が2つ目のフェッチを作成する前に1つ目のフェッチの完了をawaitしてしまうと、2つ目の処理は1つ目とオーバーラップできません。複数のタスクを結果待ちの前に作成しておけば、スケジューラに調整可能な独立した仕事を与えることができます。
タスクの所有権は、タスクの作成と同じくらい重要です。誰かが結果を監視し、例外を報告し、親の処理が終わるときに作業を終了させる責任を負わなければなりません。追跡されていないバックグラウンドタスクが存在すると、有用な処理がまだ実行中であったり、見える制御フローの外側で失敗しているにもかかわらず、呼び出し元には成功メッセージだけが返される場合があります。
Pythonの コルーチンとタスクのライフサイクル には、構造化された所有権のためのTaskGroupが含まれています。タスクグループは、そのコンテキストを抜けるときに子タスクの終了を待ちます。子タスクがキャンセル以外の例外で失敗した場合、グループは残りの子タスクをキャンセルし、ドキュメント化された例外処理を通じて失敗を報告します。この挙動は、成功が1つの親ジョブに属する一連の処理に適しています。
一方で、アプリケーションによっては独立した結果が必要な場合もあります。複数の無関係な公開ページをチェックするレポートでは、成功した観測結果を保持し、失敗した観測結果を別途記録するかもしれません。その方針は、調整プリミティブを選ぶ前に決めておくべきです。そうしないと、アプリケーションが有用な結果を誤って破棄したり、不完全な収集結果を完全なものとして提示してしまう可能性があります。
なぜ並行処理には容量制限が必要なのか
並行実行の上限は、アプリケーションがクライアント、接続先、ストレージの許容量を超えないようにする役割を果たします。見つかったすべてのURLに対してタスクを作成すると、最初の応答が届く前にメモリを消費し尽くすことがあります。ソケット数に制限があっても、キューにたまったタスクオブジェクト、応答バッファ、保留中レコードは増え続ける可能性があります。
セマフォは、保護された領域内の処理数を制限できます。そのスコープは、実際に制限したいリソース全体をカバーすべきです。接続確立の部分だけをカバーし、応答を消費する前に解放してしまうと、大きなレスポンスボディが制限なく蓄積されるおそれがあります。意図した単位がリクエスト全体のライフサイクルであるなら、その全体を制限の内側に収めておきましょう。
有界キューは段階間にバックプレッシャーを加えます。キューが満杯のとき、プロデューサーは無制限に仕事を見つけ続けるのではなく待機します。 asyncio キューの契約 は、アイテムを取得することと、その処理が完了したとマークすることを区別します。これは、プロデューサー側の作業が完了したと見なされる前に、ワーカーがレコードを検証して保存しなければならない場合に有用です。
ブロッキングコードが問題を引き起こす場面
ブロッキングコードは、ブロッキング処理が実行されている間、イベントループスレッドが他のタスクに対応することを妨げます。一般的な例としては、同期的な HTTP 呼び出し、遅いファイル操作、長時間かかるパースや変換ルーチンがあります。そのような呼び出しを非同期ラッパーで包んでも、呼び出し自体の挙動は変わりません。
ライブラリがネイティブな非同期クライアントをサポートしている場合は、それを使用してください。既存のブロッキング I/O 関数については、asyncio.to_thread を使うことで、その関数を別スレッドに移し、イベントループスレッドを空けておくことができます。これは特定の操作のためのブリッジであり、プログラム全体が非同期化されたりスレッドセーフになったりすることを保証するものではありません。
CPU 集約的な処理は、別途判断が必要です。Python のランタイムや利用するライブラリによっては、大規模な計算にプロセスベースのワーカーを使うのが適切な場合があります。そのワーカーとの間でデータを送受信するコストも考慮してください。巨大なパース済みオブジェクトをプロセス間で送ると、計算処理を分散する利点が失われることがあります。
asyncio の開発ガイダンス は、デバッグ機能とブロッキングコードに関する懸念事項を説明しています。調査の際は、リモートの応答時間とは別に、タスクの所要時間とイベントループの応答性を記録してください。エンドツーエンドの処理が遅い場合、その原因はウェブサイトではなくアプリケーション側の待ち時間かもしれません。
キャンセル、期限、リソースのクリーンアップ
キャンセルは非同期処理に対してキャンセル可能なポイントで停止を要請するものであり、リモート操作が受信されなかったことの証明にはなりません。クライアントが書き込みを送信して待機中にキャンセルしたとしても、サーバー側ではすでに書き込みが適用されている可能性があります。ワークフローは、キャンセルをロールバックとして扱うのではなく、その不確実性を表現しなければなりません。
タスクが所有するリソースにはクリーンアップ経路を用意してください。非同期コンテキストマネージャーを使えば、そのスコープが終わったタイミングでクライアントやレスポンスストリームをクローズできます。finally ブロックは、タスクがキャンセル経路で終了した場合でもアプリケーションリソースを解放できます。明確な所有ポリシーなしにキャンセルを握りつぶすことは避けてください。構造化されたタスク管理はキャンセルが正しく伝播することに依存しています。
ネットワークのタイムアウトとワークフローの期限は、異なる問いに答えるものです。クライアントのタイムアウトは、ネットワークフェーズの待ち時間の上限を定めます。ワークフローの期限は、キューイングや処理を含む、より大きな処理全体の存続期間の上限を定めます。どこで期限に達したかを記録し、オペレーターが、ローカルキューの過負荷と宛先側の遅延を区別できるようにしてください。
Web データパイプラインにおける asyncio の位置づけ
asyncio は、依存先が非同期操作を公開しているパイプラインのオーケストレーション層に適合します。有用なレイアウトとしては、探索、取得、検証、保存を分離する構成があります。各ステージは明確な入力を受け取り、元のアイテムと結び付けられるだけの同一性を持つ結果を生成します。
仮想的なカタログチェックを例にすると、要求した URL、最終的な URL、観測時刻、レスポンスステータス、パース結果を保持します。期待した商品フィールドを含まない HTML を返すページは、無効な観測として記録すべきです。スケジューリングを高速化しても、そのレスポンス自体が有用になるわけではありません。何も抽出できなかった結果と、正当な空コレクションとは、異なる結果です。
HTTP クライアントは、その意図されたライフタイムの範囲内で再利用し、セッション状態は意図的に管理してください。 HTTPX proxy configuration の議論は、関連するクライアントの文脈を提供しており、一方で現在の Scrapeless proxy connection model はネットワーク層を説明しています。プロキシのルーティングとタスクスケジューリングは依然として別個の制御であり、プロキシを追加してもアプリケーションの同時実行性は制限されません。
「 Scrapeless Proxies」を選ぶ際には、クライアント設計とあわせて、必要な地理的要件や接続の継続性を評価してください。選択したプロダクトについては見積もりを行う前に Scrapeless pricing を参照してください。見積もりは、検証に失敗したコンテンツも含め、実際のリクエストおよびレスポンスのワークロードに基づいて行ってください。
同期設計の方が適しているのはいつか?
独立した I/O のオーバーラップがほとんどないジョブでは、同期設計の方が保守しやすいことがよくあります。1 件のドキュメントを取得してすぐにパースするだけの短いコマンドでは、イベントループによる調停から得られるメリットはほとんどないかもしれません。既存の同期的な依存関係がある場合、コードの一部だけを非同期化しようとすると、元のコードよりも複雑になることもあります。
有意な待ち時間を特定し、独立した作業を分離し、そのライフタイムを管理できる場合は asyncio を選択してください。操作が本質的に逐次的である場合や、計算処理が実行時間の大部分を占める場合は、より単純な設計を選びましょう。この判断は、「非同期コードの方が速い」という一般論ではなく、ワークロードのプロファイルに基づくべきです。
結論
asyncio は、明示的なサスペンドとタスクスケジューリングを通じて、待機状態の作業を調整します。制限された収集ジョブから始め、各タスクに所有者を割り当て、ネットワーク待機とローカル処理を分離してください。ワークロードを増やす前に、シャットダウン時の挙動と結果の検証方法を見直しましょう。これらの選択が、並行性が信頼できるデータを生み出すかどうかを左右します。
あなたの並行コレクターを接続しましょう
制限付きの Python 収集ワークフローを、必要なプロキシルートおよびセッションポリシーと組み合わせてください。
今すぐ登録して $5 in free credit — no credit card required.
$5 クレジットを受け取る →FAQ
asyncio は Python の一部ですか?
asyncio は Python 標準ライブラリの一部です。サードパーティの HTTP クライアントやデータベースクライアントは、その上に構築されている場合がありますが、それらは独立したパッケージであり、それぞれ独自の接続動作や設定を持ちます。
asyncio は Python コードを複数の CPU コアで実行しますか?
asyncio のスケジューリングだけでは、Python の実行を複数の CPU コアに分散することはできません。イベントループは自分のスレッド内でタスクを実行し、別途エグゼキューターやプロセスを利用することで、別の実行コンテキストを必要とする作業を処理できます。
すべての await が別のタスクへの切り替えを引き起こすのですか?
await 式は常に別のタスクに切り替わるとは限りません。即座に完了する awaitable は、中断することなく現在の実行パスで処理を続行する場合があります。
asyncio はプロキシのローテーションを制御できますか?
asyncio はプロキシを使用する処理をスケジューリングできますが、ルーティングとローテーションの挙動を決定するのはクライアントとプロキシプロバイダです。セッションポリシーはアプリケーションの並行性制御とは分離しておきましょう。
なぜ非同期アプリケーションなのに応答しなくなるのですか?
非同期アプリケーションでも、ブロッキングな呼び出しや長時間の計算がイベントループスレッドを専有すると、応答しなくなることがあります。あらゆる遅延をリモートサービスのせいにする前に、ローカル処理、キューの増加、接続数の制限を確認してください。