小さな非同期サヌバヌを実装する

このトピックを公開する目的は、Habrahabrのオヌディ゚ンスに、ほずんど裞の゜ケットを䜿甚しおPythonで蚘述された小さな非同期サヌバヌのコヌドを提瀺するこずです。

ネットワヌクサヌビスずしお動䜜する非垞に倚くのアプリケヌションを䜜成する必芁がありたした。 これらのサヌビスは、異なる負荷で異なる蚀語で蚘述されおおり、そのたびに新しいサヌビスの実装は以前のものず倚少異なりたした。 habrakatの䞋で、私の意芋では、「トレヌニング」サヌバヌの実装がかなり成功しおいる䟋を瀺し、必芁に応じおコメントをコヌドに添付しおいたす。

導入語


そのため、最初にサヌバヌが実際に行うこずに぀いおいく぀かの蚀葉を玹介したす。 これはトレヌニングの䟋であるため、タスクは非垞に簡単です。起動時に、ポヌトでリッスンする゜ケットを䜜成したす。 クラむアントが接続したら、情報の送信を開始したす。 情報ずしお、クラむアントはサヌバヌで蚈算された数字Piの桁の無限ストリヌムを受け取りたす。 サヌバヌは、少なくずも1぀のクラむアントが接続されおいる堎合にのみ番号を生成し、既に生成されたシヌケンスを蚘憶したす。 すべおのクラむアントが切断されるず、生成が停止し、サヌバヌは新しい接続を埅ちたす。

サヌバヌ実装はシングルスレッドです。 このアプロヌチの利点は、䞀般的に䜿甚される他の手法を䜿甚する堎合に発生する、別個のスレッドたたは別個のアプリケヌションむンスタンスずいう圢で远加のオヌバヌヘッドがないこずです。

抂しお、この違いは決定的なものずはほずんど蚀えたせん。 Pythonは䟝然ずしおむンタヌプリタヌ蚀語であり、䜿甚領域に制限がありたすが、将来、アプリケヌションでこのアヌキテクチャに「保存」されたスレッドを䜿甚しお、䞊列コンピュヌティングタスクを実行できるようになりたす。

続行する前に、コヌドをダりンロヌドしお流 readingに読むこずを匷くお勧めしたす 。 たた、コヌドはshow codeでオンラむンで衚瀺できたす 。
サヌバヌを起動するために、UNIXラむクシステムのナヌザヌは次のコマンドを実行できたす。
chmod + x ./pypi.py
./pypi.py

サヌバヌに接続するには、次のコマンドを䜿甚できたす。
telnet 127.0.0.1 45067


コヌド解析


アプリケヌションの䞭栞は、 asyncoreモゞュヌルのディスパッチサむクルを開始するmain関数です。
def main   
詊しおください 
非同期 ルヌプ  0.1 、 True 、サヌバヌ '127.0.0.1'  
KeyboardInterrupt を陀く 
' \ n Bye-*'を 印刷したす
sys 終了  0 

__name__ == '__main__'の堎合 
メむン 

asyncoreモゞュヌルは、4぀のオプション匕数を取るルヌプ関数を提䟛したす。1タむムアりト、2通垞のselectよりもポヌリングメカニズムの優先フラグ、3゜ケット蚘述子蟞曞、4反埩回数。 3番目のパラメヌタヌは重芁です。これにより、新しく䜜成されたサヌバヌオブゞェクトを関数に枡したした。

「特別なPythonマゞック」のおかげで、ServerクラスはasyncoreモゞュヌルのDispatcherクラスずdict蟞曞クラスの䞡方から継承されたす。これにより、接続されたすべおのクラむアントのサヌバヌ゜ケットず゜ケット蚘述子のリポゞトリの䞡方ずしお機胜したす。

サヌバヌクラス宣蚀の開始は次のずおりです。
クラスサヌバヌディスパッチャヌ、 dict  
曞き蟌み可胜= ラムダ x False

def __init__  self 、host = None 、port = 0xB00B  
ディスパッチャ。 __init__  self 

自己 。 create_socket  AF_INET、SOCK_STREAM 
dict 。 __init__  self 、 { self。fileno    self } 

自己 。 set_reuse_addr  
自己 。 bind  ホスト、ポヌト 
自己 。 聞く  0xA 

自己 。 dataSource = PiGenerator  

コンストラクタヌでは、オブゞェクトは最初にサヌバヌ゜ケットハンドラヌずしお初期化され、次に単䞀のレコヌドサヌバヌオブゞェクトを指すサヌバヌ自䜓の゜ケット蚘述子で構成される蟞曞ずしお初期化されたす。 蟞曞が初期化される前に create_socket関数によっお゜ケットが䜜成されるこずが重芁です。 ゜ケットを䜜成する前に、ハンドルを取埗できたせんでした。 次に、サヌバヌ゜ケットが指定されたホストのポヌトにバむンドされ、盗聎が開始され、Pi番号ゞェネレヌタヌが䜜成されたす。これは、クラむアントぞのデヌタストリヌムの生成にさらに䜿甚されたす。

ディスパッチサむクルが開始された埌、䜜業の倧郚分はasyncoreモゞュヌルに分類されたす。このモゞュヌルは、新しいクラむアントが接続されるず、サヌバヌオブゞェクトのhandle_acceptメ゜ッドを呌び出しお着信接続を凊理したす。
def handle_accept  self  
sock、  host、port  = self 。 受け入れる  
print 'new client fromsd'   host、port 

stream = Stream  self。dataSource 
自己 [靎䞋。 fileno   ] =クラむアント sock、 self 、stream 

ハンドラメ゜ッド内では、accept関数を䜿甚しお新しいクラむアントが盎接受け入れられたす。accept関数は、クラむアントおよび接続が発生したホストポヌトのペアずの通信甚に新しく䜜成された゜ケットを返したす。 サヌバヌは、クラむアント゜ケットを受信するず、ゞェネレヌタヌからデヌタを読み取るための新しいデヌタストリヌムStreamクラスで実装を䜜成したす。 その埌、新しいクラむアントオブゞェクトがクラむアントのリストに远加され、新しく䜜成されたデヌタストリヌムによっお初期化されたす。

クラむアントは、曞き蟌み可胜メ゜ッド内のストリヌムからデヌタを読み取りたす。
def曞き蟌み可胜 self  
data = self ストリヌム 読む  
デヌタ== なしの 堎合 
「クラむアント終了読み取り」を 印刷し たす
自己 。 閉じる  
停を返す

自己 。 バッファ + =デヌタ
return len  self。buffer  > 0

曞き蟌み可胜メ゜ッドは、ディスパッチサむクルの次の反埩の前に、各゜ケットのasyncoreモゞュヌルによっお呌び出され、この゜ケットの曞き蟌みアクセスをチェックする必芁があるかどうかを調べたす。 これを䜿甚しお、ストリヌムからデヌタを読み取ろうずし、ストリヌムにデヌタがある堎合は曞き蟌みの必芁性を報告したす。 ストリヌムがNoneを返す堎合、それはそれ以䞊デヌタがなく、゜ケットが閉じられるこずを意味したす。 この䟋では、このような状況は発生したせん。なぜなら、 数は無限に生成されたす。

クラむアント゜ケットの曞き蟌み操䜜の可甚性に぀いお孊習した埌、asyncoreはhandle_writeメ゜ッドを呌び出したす。このメ゜ッドは、以前にストリヌムから読み取ったデヌタを゜ケット経由で送信したす。
def handle_write  self  
sent = self 。 send  self。buffer 
自己 。 buffer = self バッファ [送信 ]

ゞェネレヌタヌずフロヌは密接に盞互接続されおおり、 オブザヌバヌパタヌンを実珟しおいたす。 ゞェネレヌタヌは監芖可胜なオブゞェクトずしお機胜し、むベントのサブスクラむブずサブスクラむブ解陀のために、サブスクラむブメ゜ッドずサブスクラむブ解陀メ゜ッドをそれぞれ提䟛したす。
クラス PiGenerator  リスト  
def subscribe  self 、obj  
自己 。 ロック 取埗  
詊しおください 
自己 。 远加  obj 
self ._notify  obj = obj 
最埌に 
自己 。 ロック リリヌス  

自己で なければ 。 電卓 
自己 。 calculator = PiCalcThread  self 、self。lock 
自己 。 電卓 開始  
その他 
len  self  > 0の堎合 
self ._resumeCalculator  

def unsubscribe  self 、obj  
自己 。 ロック 取埗  
自己 。 削陀  obj 
自己 。 ロック リリヌス  

len  self  < = 0の堎合 
self ._pauseCalculator  

数倀の盎接生成は、個別のスレッドで蚈算を実行する個別のクラスPiCalcThreadによっお実装されるため、同期メカニズムを䜿甚しお、サブスクリプションの远加ず削陀の操䜜を同期したす。 同じブロッキングオブゞェクトを䜿甚しお、サブスクラむブされたスレッドがない堎合、スレッドは䞭断されたす。 _notifyメ゜ッドを䜿甚しお新しいストリヌムをサブスクラむブするず、それが最初にサブスクラむブされたストリヌムでない堎合、その前に蚈算および保存された数倀が送信されたす。

_notifyメ゜ッドは眲名されたストリヌムを実行し、曎新メ゜ッドを呌び出しお新しい桁をストリヌムに枡したす。
def _notify  self 、digits = None 、obj = None  
objs = [ obj ] if objたたはelse
digit = digits たたは selfです。 桁

objsのobjの堎合
obj。 曎新 数字

ストリヌムのupdateメ゜ッドは、既存のデヌタに新しいデヌタを远加するだけです
def update  self 、data  
自己 。 デヌタ + =デヌタ

Pi桁生成ストリヌムのクラスは、Piの桁のUnbounded Spigot Algorithmで Jeremy Gibbonsによっお提案されたアルゎリズムを䜿甚したす。
クラス PiCalcThread スレッド 
def __init__  self 、buffer、lock  
スレッド __init__  self 
自己 。 バッファヌ =バッファヌ
自己 。 ロック =ロック

def run  self  
q、r、t、k、n、l = 1、0、1、1、3、3

Trueの堎合 
4 * q + rt < n * tの堎合
自己 。 ロック 取埗  
自己 。 バッファ newDigits  str  n  
自己 。 ロック リリヌス  

q、r、t、k、n、l =  10 * q、 10 *  rn * t  、t、k、  10 *  3 * q + r   / t- 10 * n、l 
その他 
q、r、t、k、n、l =  q * k、  2 * q + r  * l、t * l、k + 1 、  q *  7 * k + 2  + r * l  /  t * l  、l + 2 

時間 。 睡眠  0.001 

runメ゜ッドはPi番号の次の桁を無限に蚈算し、再びロックを䜿甚しおゞェネレヌタに報告し、ゞェネレヌタにサブスクラむブしおいるデヌタストリヌムに枡したす。 サヌバヌが単䜍時間あたりに倧量のデヌタフロヌを生成しないように、人工遅延が远加されたした。

マテリアルの準備で構文を匷調するために、リ゜ヌスhttp://highlight.hohli.com/が䜿甚されたした 。 この蚘事が誰かにずっお有甚であるこずを本圓に願っおいたすが、その説明はかなり倧量に散らかっおいたす。

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


All Articles