しかし、同じタスク(というよりは、その主要部分-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たぶん誰かがあなたが生産的に使えるものについて考えている
非同期クライアント? :)