2017-04-25 12 views
0

私はpython3 + asyncioを使って複数プロセスのサーバープログラムを作成しようとしていますが、AbstractEventLoop.create_serverに 'reuse_port'という名前のパラメータがあることがわかりました。python3 asyncio reuse_port経由で正しいマルチプロセスサーバープログラムを書くには?

私はいくつかのコードを書いたので、マルチプロセスを使っていくつかのプロセスを作成し、各プロセスはasyncioイベントループを作成し、これらのプロセスはすべて同じポートでリッスンします。

私はこれらのプロセスが連携してリクエストに応答すると思っていましたが、このサーバープログラムをテストすると、常に1つのプロセスしかリクエストに応答しませんでした。

他のプロセスが要求に応答しないのはなぜですか? 私のコードにバグはありますか?

OSX10.11 + PYTHON3.5.2

サーバー:

#!/usr/bin/env python 
# -*- coding: UTF-8 -*- 

import os 
import sys 
import multiprocessing 
import asyncio 
import socket 

tcp_listen_port = 44330 


class Listener: 
    def __init__(self, protocol, listen_port, listen_host='localhost'): 
     self._protocol = protocol 
     self._listen_port = listen_port 
     self._listen_host = listen_host 
     self._loop = None 
     self._server = None 
     self._pid = os.getpid() 

    def run(self): 
     asyncio.set_event_loop(asyncio.new_event_loop()) 
     self._loop = asyncio.get_event_loop() 

     coro = self._loop.create_server(
      self._protocol, 
      host=self._listen_host, 
      port=self._listen_port, 
      family=socket.AF_INET, 
      reuse_port=True 
     ) 

     self._server = self._loop.run_until_complete(coro) 

     print('Listener Server on {}, pid {}'.format(
      self._server.sockets[0].getsockname(), 
      self._pid 
      )) 
     self._loop.run_forever() 

    def close(self): 
     self._server.close() 
     self._loop.run_until_complete(self._server.wait_closed()) 
     self._loop.close() 


class ProtocolEcho(asyncio.Protocol): 
    def connection_made(self, transport): 
     self.transport = transport 

    def data_received(self, data): 
     data = 'hello python asyncio from pid {}\r\n\r\n'.format(os.getpid()).encode() 
     self.transport.write(data) 
     self.transport.close() 


def create_tcp_srv(listen_port): 
    listener = Listener(ProtocolEcho, listen_port) 
    try: 
     listener.run() 
    except KeyboardInterrupt: 
     listener.close() 


def main(): 
    cpu_count = multiprocessing.cpu_count() 

    srvproclist = list() 
    for i in range(cpu_count): 
     p = multiprocessing.Process(
      target=create_tcp_srv, 
      args=(tcp_listen_port,) 
      ) 
     srvproclist.append(p) 

    for proc in srvproclist: 
     proc.start() 

    for proc in srvproclist: 
     proc.join() 


if __name__ == '__main__': 
    main() 

クライアント

#!/usr/bin/env python 
# -*- coding: UTF-8 -*- 

import os 
import asyncio 
import multiprocessing 

async def req(): 
    connect = asyncio.open_connection('localhost', 44330) 
    reader, writer = await connect 

    writer.write('hello'.encode('utf-8')) 
    await writer.drain() 

    while True: 
     line = await reader.readline() 

     if line == b'\r\n': 
      break 

     print('proc {} recv {}'.format(os.getpid(), line.decode())) 

    writer.close() 


def begin_test(): 
    asyncio.set_event_loop(asyncio.new_event_loop()) 
    loop = asyncio.get_event_loop() 
    tasks = [req() for i in range(10)] 
    loop.run_until_complete(asyncio.wait(tasks)) 
    loop.close() 


plist = list() 
for i in range(4): 
    p = multiprocessing.Process(
     target=begin_test 
     ) 
    plist.append(p) 

for proc in plist: 
    proc.start() 

for proc in plist: 
    proc.join() 

このようなクライアント出力:

proc 72319 recv hello python asyncio from pid 72310 
proc 72318 recv hello python asyncio from pid 72310 
proc 72319 recv hello python asyncio from pid 72310 
proc 72320 recv hello python asyncio from pid 72310 
proc 72321 recv hello python asyncio from pid 72310 
proc 72319 recv hello python asyncio from pid 72310 
proc 72318 recv hello python asyncio from pid 72310 
proc 72320 recv hello python asyncio from pid 72310 
proc 72321 recv hello python asyncio from pid 72310 
proc 72318 recv hello python asyncio from pid 72310 
proc 72320 recv hello python asyncio from pid 72310 
proc 72321 recv hello python asyncio from pid 72310 

最後の数字「72310」は私の要求に応えるサーバープロセスのPIDです。そのため、プロセスが1つしかないと思っていました。しかし、なぜ...

答えて

0

一度に2つ以上のことを行うプログラムを取得するにはかなりの労力がかかります。終わりのないプロセスが1つあるサーバーを1つ作成しており、プログラムにコールバック(connection_made()、data_received()など)をガイドしていません。しかし、コルーチンの使用で簡単な修正です。

単純に言えば、追加のコールバックを呼び出すイベントはないようです。

スクリプト内にコルーチンを実装すると、プロセスごとに同じ文字列を返すように見えるようになります。あなたがしなければならないことは、各関数(プロセス)をコルーチンのデコレータ(@ asyncio.coroutine)にラップして、コルーチンプログラムをどのようにフィットするかで設定することです。

thisをチェックしてください。 asyncioの素晴らしい記事です。 Asyncioは複数のことを同時に行うことのできるプログラムを作っているので、誇大宣伝をたくさんしています。

「yield from」の実装は特に素晴らしいです。

注:私はこれをコメントに入れましたが、私はまだ通りのクレジットは持っていません。ある日....

関連する問題