非同期HTTPクライアント、またはマルチスレッドが不要な理由

少し前に Python上のサイトのクライアントパーサーに関するメモがHabréにジャンプしました。 この例の著者は、マルチスレッドネットワークアプリケーションの問題を分析しました。

しかし、同じタスク(というよりは、その主要部分-httpサーバーとの並列接続)は、フローなしで効果的に解決できるように思えました。



はじめに、TwistedとTornadoについての記事を見て、頭をかき、ノンブロッキングソケットのドキュメントを掘り下げて、非同期httpクライアントサーバーをスケッチしまし

以下に、アプリケーションの主要部分のソースコードと説明を示します。

インポートソケット
インポート選択
インポートシステム
インポートエラー
輸入時間

設定インポート から *

def ioloop ip_source request_source
"" "非同期ループ

ip_source-無限反復可能、接続用のIPアドレスを発行します。
request_source-反復可能で、リクエストボディを生成します;
» ""
starttime = time 時間

#ソケットプールを開きます。 接続本体の要求と応答を格納する辞書
epoll = select エポール
接続= { } ; 応答= { } ; リクエスト= { }
バイト送信= 0.0
バイト読み取り= 0.0
タイムアウト= 0.3

#最初のリクエストを選択
request = request_source 次へ
試してください
Trueの場合
#最小数よりも少ない場合は、接続数を確認します
#可能ですが、まだリクエストがあります-もう1つ追加します。

connection_num = len connections

connection_num < CLIENT_NUM および requestの場合
ip = ip_source 次へ
print "%sへの接続を開きます。" ip
clientsocket = socket ソケット socket。AF_INET
ソケット SOCK_STREAM
#やや非自明。 ノンブロッキングソケットスロー
すぐに接続できない場合は#EINPROGRESSエラー例外。
#エラーを無視して、ソケット上のイベントの待機を開始します。

clientsocket セットブロッキング 0
試してください
res = clientsocket 接続 ip 80
ソケットを除く エラーエラー

if err errno = errno アインプロレス
上げる
#ソケットをプールと接続辞書に追加します
epoll 登録 clientsocket .fileno 、. EPOLLOUTを選択
接続[ clientsocket fileno ] = clientsocket
要求[ clientsocket fileno ] =リクエスト
応答[ clientsocket fileno ] = ""

#「プール」-つまり、イベントのコレクション

events = epoll ポーリングタイムアウト
fileno イベントのイベント
イベントの場合&を選択します EPOLLOUT
#リクエストの一部を送信しています...

試してください
byteswritten = connections [ fileno ] 送信リクエスト[ fileno ]
リクエスト[ fileno ] =リクエスト[ fileno ] [ byteswritten ]
byteswritten 「送信バイト数」を出力します。
bytessent + = byteswritten
if len requests [ fileno ] = = 0
epoll 変更 fileno .EPOLLIN 選択
「読み取りに切り替えられました。」
ソケットを除く エラーエラー
print "Socket write error:„ err
Exception err を除く
「不明なソケットエラー:„ エラー
elifイベント&を選択します エポリン
#回答の一部を読む...“

試してください
バイト=接続[ fileno ] recv 1024
ソケットを除く エラーエラー
#「ピアによる接続リセット」エラーをキャッチ-
#多数の接続で発生

if err errno = = errno ECONNRESET
epoll 登録解除 fileno
接続[ fileno ] 閉じる
del connections [ fileno ]
印刷 »ピアによる接続のリセット。」
続ける
その他
エラーを上げる

print len bytes 「読み取られたバイト」。
bytesread + = len バイト
応答[ fileno ] + =バイト
バイトでない 場合
epoll 登録解除 fileno
接続[ fileno ] 閉じる ;
del connections [ fileno ]
「読み取り完了...終了」を 印刷 します。

#次のクエリを選択
リクエストの場合
request = request_source 次へ

「接続が残っている:„ len connections
len 接続)で ない 場合
破る
KeyboardInterrupt を除く
「信号によって中断されたループ」を 印刷します
fdの場合 は、接続の靴下 アイテム
靴下 閉じる
epoll 閉じる

endtime = time 時間
timespent = endtime - starttime
応答を返す timespent bytesread bytessent

ここでの教訓は単純です-ストリームをどこにでも押し込むべきではありません;さらに、マルチスレッドはプログラムの信頼性を低下させるだけで、テストで既知の問題を作成し、とらえどころのないバグの原因になる場合があります。 パフォーマンスは重要ではないが、何かを並列化したい場合は、通常のプロセスやプリミティブIPCでさえ正当化されることがよくあります。

さらに、Pythonには実際のカーネルレベルのスレッドはまだありませんが、いまいましいGILはまだ生きています。 したがって、マルチコアプロセッサではパフォーマンス上の利点は得られません。

もちろん、このスクリプトは不気味で急いで実行され、サーバーによる接続の中断やソケットへの読み取り/書き込み操作のエラーを処理せず、サーバーの応答を解析しませんが、cnn.comサイトのルートを何度もチャンネルの機能の限界-800-1000までドラッグしますKb / s :)

スクリプトソース全体はここのどこかにあります。

PSたぶん誰かがあなたが生産的に使えるものについて考えている
非同期クライアント? :)

Source: https://habr.com/ru/post/J81716/


All Articles