少し前に
、 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たぶん誰かがあなたが生産的に使えるものについて考えている
非同期クライアント? :)