非同期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たぶん誰かがあなたが生産的に使えるものについて考えている

非同期クライアント? :)



All Articles