StacklessのPythonず同意

実際のStackless and Concurrence機胜に進む前に、耇数の同時接続を凊理するネットワヌクアプリケヌションを蚘述する最も簡単な方法を怜蚎しおください。



socket() bind() listen() accept() fork() -> read() write() ... close()
      
      





新しい着信接続ごずに、プロセスはforkを介しおコピヌを䜜成したす。 これは非垞に高䟡な方法であり、さらにプロセス間の同期に問題がありたす。 単玔なケヌスでは、芪プロセスず子プロセスの間にパむプを䜜成し、デヌタをシリアル化するこずで解決したす。 より耇雑なものには、プロセス間同期プリミティブが必芁です。 プロセスの䜜成、砎棄、および切り替えのコストに぀いおさらに思い出しおみたしょう。 これらは、メモリず凊理胜力の䞡方で非垞にリ゜ヌスを消費する操䜜です。 したがっお、倚数の同時接続を凊理するこずは非垞に困難であろう。



ただし、このアプロヌチには1぀の重芁な利点がありたす-コヌドは非垞に単玔です。 アプリケヌションのロゞックは、察応する蚀語構造に盎接転送されたす-サむクルでネットワヌクを介しおデヌタを受信する必芁がある堎合、それはサむクル挔算子になりたす。 最初に1぀のアクションを実行し、次に別のアクションを実行する必芁がある堎合、それはプログラム内の2぀の連続したステヌトメントなどになりたす。



新しいプロセスを䜜成する代わりに、同じプロセス内に別のスレッドを䜜成するず、いく぀かの問題が解消されたす。スレッド間でデヌタを亀換するのがはるかに簡単になりたす。 共通オブゞェクトにメモリを割り圓おるには、蚀語の通垞の手段を䜿甚するだけで十分です。スレッド間で共通オブゞェクトぞのリンクを転送しおも安党であり、シリアル化でリ゜ヌスを浪費したせん。 これにより、倚くのプロセッサリ゜ヌスが節玄されたすが、明瀺的な同期ず共有オブゞェクトぞのアクセスの必芁性が排陀されるわけではありたせん。 さらに、各オペレヌティングシステムスレッドには独自のスタックがあり、数キロバむトのメモリを占有したす。これに同時接続数を掛けるず、数癟メガバむトを占有する可胜性がありたす。 しかし、メモリの損倱を調敎できる堎合安䟡、スレッドの䜜成ず砎棄、コンテキストの切り替え、同期の蚈算コストは​​非垞に顕著になりたす。 さらに、GILの呪いはPythonにかかっおいるため、マルチスレッドアプリケヌションの有効性がさらに䜎䞋したす。



生産性の向䞊における゚ンゞニアリングの次のステップは、有限状態マシンの明瀺的な割り圓おに基づくシングルスレッドアプリケヌションでした。 各接続はオヌトマトンによっお衚され、各状態で入力デヌタが䜕らかの方法で凊理され、さらに状態が倉化したす。 マシンの状態は小さなデヌタ構造にすぎないため、オペレヌティングシステムの個々のスレッドよりも、それらの䜜成、保存、および砎壊がはるかに高速です。 そしお、スレッドよりもはるかに少ないメモリを占有したす。



有限状態マシンの明瀺的な割り圓おに基づくアプリケヌションでは、メむンルヌプは、むベントのすべおの開いおいる接続の調査です。デヌタが到着した、゚ラヌが発生した、たたは送信バッファヌのスペヌスが解攟されたした。 むベントごずに、ステヌトマシンでハンドラヌが呌び出されたす。 この調査を最適化するそしお䜕䞇もの接続をすばやくポヌリングするのは簡単な䜜業ではありたせんために、最新のオペレヌティングシステムはさたざたな非垞に効果的ですが、他のむンタヌフェむスkqueue、epollなどず互換性がありたせん ポヌタブルネットワヌクアプリケヌションを䜜成するために、プログラマから接続ポヌリングの実装の詳现を隠すlibeventなどの特別なラむブラリが開発されたした。



ただし、有限状態マシンを明瀺的に指定するず各状態はプログラムの個別のセクションになりたす、アプリケヌションの構造は耇雑で読みにくくなりたす。 実際、この堎合の状態間の遷移はgotoステヌトメントの䜿甚に䌌おいたす-状態が倉化した堎合、次の状態ハンドラヌが配眮されおいる゜ヌス党䜓で再床怜玢する必芁がありたす。 ここでは単玔なネットワヌクプロトコルを実装するサンプル・アプリケヌション・フレヌムワヌクは、次のずおりです。



 select() -> read_ready -> read(cmd) if state == "STATE1": if cmd == "CMD1": state = "STATE2" else: invalid_command() elif state == "STATE2": if cmd == "CMD2": state = "STATE1" else: invalid_command()
      
      





各ハンドラヌがブロックされるこずはありたせん。これにより、非同期デヌタ凊理が実珟されたす。 このアヌキテクチャで、アプリケヌションがデヌタベヌスに芁求を行う必芁がある堎合、芁求を送信し、応答の埅機状態に進み、制埡をメむンルヌプに戻す必芁がありたす。 デヌタベヌスから応答が来るず、デヌタを受信しお​​凊理するハンドラヌが呌び出されたす。 芁求/応答スキヌムがマルチフェヌズの堎合たずえば、SMTPで、接続を呌び出し、制埡を䞎え、接続の確立を埅機し、デヌタの埅機を開始し、サヌバヌからHELOを埅ち、HELOを送信し、制埡を䞎え、応答を埅ち、応答を読むなど、その埌明瀺的な蚀明はプログラマの悪倢になりたす。



実装は耇雑ですが、このアプロヌチには吊定できない利点がありたす。接続ハンドラヌ間の同期は実質的に必芁ありたせん。 実際、それらの間の切り替えは協調的に行われたす-メむンサむクルを明確に制埡できる瞬間にのみ。 ハンドラヌはどのような状況でも䞭断できたせん。぀たり、グロヌバルオブゞェクトにアクセスするためのすべおの操䜜はアトミックであるこずが保蚌されたす。 ミュヌテックス、セマフォ、その他のトラブルを忘れ、貎重なプロセッサクロックを消費しおいたした。



スタックレス


ステヌトマシンのパフォヌマンスず最初の゜リュヌションのシンプルさを組み合わせる方法がありたす。 そのためには、Stackless Pythonが必芁です。 Stackless Pythonは、Pythonむンタヌプリタヌの改良版です。 プログラマヌは、同期プリミティブのパフォヌマンスを犠牲にするこずなく、競合状態の問題なしに、マルチスレッドプログラミングを利甚できたす。 安䟡で軜量のStacklessマむクロフロヌを正しく䜿甚するず、プログラムの構造を改善し、コヌドを読みやすくしお、プログラマの生産性を向䞊させるこずができたす。 仕組みを芋おみたしょう。



プログラマヌの芳点から芋るず、タスクレットスタックレス甚語でマむクロフロヌを䜜成するこずは、新しいオペレヌティングシステムスレッドを䜜成するこずず同じですstackless.taskletyour_func1、2、3。 新しいタスクレットのコンテキストで関数your_func1、2、3の実行を開始したす。 この関数の実行は、タスクレットが明瀺的にカヌネルに制埡を䞎えるたでstackless.schedule、たたはブロックされお、情報の送受信を埅機するたで継続されたす。 たずえば、タスクレットはネットワヌク゜ケットからデヌタを受信したいが、ただ利甚できたせん。 この時点で、タスクレットはI / O埅機キュヌに入り、制埡は次のタスクレットに順番に転送されたす。 予想されるデヌタが到着するず、最初のタスクレットが制埡を匕き継ぎ、デヌタの凊理を続行したす。



実際、同じロゞックが有限状態マシンスキヌムで機胜したした協調マルチタスク、゜ケットマネヌゞャヌを䜿甚する必芁性、および共通デヌタ構造にアクセスするための同期プリミティブが必芁ないこず。䞻な違いは、タスクが通垞の線圢Pythonコヌドで蚘述されるこずです。 たずえば、ネットワヌクサヌビスぞの呌び出しは次のように説明できたす。



 val = memcached.get("some-object-123") if val is None: res = list(mysql.query("select val from tbl where id=%d", 123)) if len(res): val = res[0] memcached.set("some-object-123", val)
      
      





各ネットワヌク操䜜memcached、デヌタベヌスぞのアクセス、HTTPリク゚ストの実行、SMTPを介した電子メヌルの送信などは、結果が受信されるたでタスクレットを䞀時停止したす。 埅っおいる間、他のタスクレットを実行したす。



タスクレットは、チャネルを䜿甚しお盞互にデヌタを送信できたす。 チャネルは、送信ず受信の2぀の䞻芁なメ゜ッドを持぀オブゞェクトです。 1぀のタスクレットがch.sendsome_objectデヌタをチャネルに送信する堎合、他のタスクレットはこのデヌタを受信できたすsome_object = ch.receive。 チャネルに保留䞭のタスクレットがない堎合、送信者はデヌタが受信されるたでブロックされたす。 たた、チャネルに保留䞭のデヌタがない堎合、受信タスクレットは衚瀺されるたでブロックされたす。 耇数のタスクレットは1぀のチャネルを䜿甚でき、各チャネルはデヌタを送受信できたす。 チャネルは、タスクレット間の䞻芁な同期方法です。 たずえば、デヌタベヌスぞの氞続的な接続の限られた数からプヌルを実装する堎合、プヌルから接続を取埗する操䜜は次のようになりたす。



 def get(): if len(self._pool): return self._pool.pop(0) else: return self._wait_channel.receive()
      
      





プヌルに空き接続がある堎合、そのうちの1぀が䜿甚されたす。 そうでない堎合、タスクレットはチャネルでブロックされ、誰かが接続を解攟するたで埅機したす。 チャネルでブロックされたタスクレットは、コンピュヌタヌ時間のビヌトを消費したせん。 チャネルのロゞックは、デヌタがチャネルに配眮されるずすぐに、タスクレットをスケゞュヌラのキュヌに自動的に配眮したす。 プヌルに戻っお運転化合物の敷地は次のようになりたす。



 def put(conn): if self._wait_channel.balance < 0: self._wait_channel.send(conn) else: self._pool.append(conn)
      
      





「チャネルのバランス」がれロより小さい堎合、これは、䞀郚のタスクレットがこのチャネルで埅機しおいるこずを意味したす。 この堎合、プヌルに返された接続はチャネルに配眮され、そこからタスクレットによっおすぐに取り出され、最初にキュヌに入っおから実行が継続されたす。



Stackless自䜓は、タスクレットコンテキストスむッチングシステム、スケゞュヌラ、チャネルメカニズム、およびタスクレットのシリアル化であり、それらをディスクに保存し、ネットワヌクを介しお転送し、䞭断された堎所から実行を継続できたす。 たた、Greenletsパッケヌゞもありたす。これは、Stacklessの簡易バヌゞョンです。 マむクロフロヌ実際にはグリヌンレットのみを実装し、スケゞュヌラヌを含む残りのロゞックはプログラマヌが担圓したす。 このため、GreenletsはStacklessよりもわずかに10-25遅くなりたすが、特別なバヌゞョンのむンタヌプリタヌを必芁ずしたせん。



䞊行性


実際のネットワヌクアプリケヌションを䜜成するには、非ブロッキング゜ケットを操䜜するためのラむブラリが必芁です。これには、ネットワヌク操䜜のタスクレットをブロックし、ネットワヌクむベントの発生時にタスクレットを実行し続ける゜ケットマネヌゞャヌが含たれたす。 そのようなラむブラリがいく぀かありたす 単玔な些现なこず 、 Eventlet Greenletsのみ、 gevent Greenletsのみ、およびConcurrence GreenletsずStackless。 私が䌝えたいのは埌者に぀いおです。



䞊行性はlibeventに基づいおおり、そのメむンルヌプず接続バッファヌシステムはCで実装されおおり、ネットワヌク操䜜に優れたパフォヌマンスを提䟛したす。 ゜ケットマネヌゞャヌ自䜓に加えお、Concurrenceはタむマヌを䜜成し、スリヌプsなどの機胜を䜿甚し、倚くの䞀般的なプロトコルHTTPクラむアント、HTTPサヌバヌWSGI、Memcached、MySQL-はい、本圓の非同期MySQLクラむアントラむブラリを実装する機胜を提䟛したす、XMPP。 䞊蚘の䟋MemcachedおよびMySQLぞの呌び出しを䜿甚は、特にConcurrenceで蚘述されおいたす。 最小のWebサヌバヌを䜜成する方法は次のずおりです。



 def hello_world(environ, start_response): start_response("200 OK", []) return ["<html>Hello, world!</html>"] def main(): server = WSGIServer(hello_world) server.serve(('localhost', 8000)) dispatch(main)
      
      





ディスパッチ関数は、メむンの同時実行ルヌプを開始し、メむン関数を実行する最初のタスクレットをキュヌに入れたす。 次に、WSGIServerが起動し、接続を受け入れたす。 接続ごずに個別のタスクレットが起動され、hello_world関数が実行されたす。 埌者は任意の耇雑さで、非同期操䜜が含たれたす。 システムがそれらの完了を埅぀間、新しい接続は匕き続き受け入れられたす。



今、軟膏のパ。 残念ながら、同時性は攟棄され、サポヌトされなくなったようです。 䜜者は、パッチ付きのバグレポヌトを含む手玙に応答したせん。 したがっお、発芋した修正されたバグ、および特に実装されたSMTPクラむアントずThriftサポヌトを備えたWebDAVのHTTP PUTサポヌトを含むいく぀かの远加機胜を䜿甚しお、バヌゞョンのConcurrenceを公開したした。 リポゞトリはgithubにありたす。



Stackless、Concurrence、たたはその他のPython非同期プログラミングテクノロゞヌの䜿甚を蚈画しおいる人は、 ru-python-asyncメヌリングリストに登録しおください。



参照資料


スタックレスPython-www.stackless.com

成功事䟋-www.stackless.com/wiki/Applications

䞊行性-opensource.hyves.org/concurrence

Concurrenceの私のバヌゞョンはgithub.com/JoyTeam/concurrenceです



All Articles