2017-04-25 1 views
0

저는 python3 + asyncio를 통해 다중 프로세스 서버 프로그램을 작성하려고합니다. 그리고 AbstractEventLoop.create_server에 'reuse_port'라는 매개 변수가 있다는 것을 발견했습니다.python3 asyncio reuse_port를 통해 올바른 다중 프로세스 서버 프로그램을 작성하는 방법?

일부 코드를 작성 했으므로 다중 프로세스를 사용하여 일부 프로세스를 작성하고 각 프로세스는 asyncio 이벤트 루프를 작성하며 이러한 모든 프로세스는 동일한 포트에서 수신 대기합니다.

이 프로세스가 함께 작동하여 요청에 응답 할 수 있다고 생각했지만이 서버 프로그램을 테스트 할 때 단 하나의 프로세스 만 내 요청에 항상 응답합니다.

다른 프로세스가 요청에 응답하지 않는 이유는 무엇입니까? 내 코드에 버그가 있습니까?

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이며 하나의 프로세스 만 작동하고 있다고 생각합니다. 그러나 왜 ...

답변

0

한 번에 두 가지 이상의 작업을 수행하는 데는 많은 작업이 필요합니다. 하나의 끝없는 프로세스로 하나의 서버를 만들고 있으며 콜백 (예 : connection_made(), data_received() 등)으로 프로그램을 안내하지 않았습니다. 그것은 coroutines의 사용과 함께 간단한 픽스입니다.

간단히 말해 추가 콜백을 호출하는 이벤트가없는 것처럼 보입니다.

스크립트에서 코 루틴을 구현하는 것은 각 프로세스에 대해 동일한 문자열을 반환하는 것처럼 보일 것입니다. 당신이해야 할 일은 coroutine 데코레이터 (@ asyncio.coroutine)에 각 함수 (프로세스)를 래핑 한 다음 coroutine 프로그램을 어떻게 보이는지 설정하는 것입니다.

this을 확인하십시오. asyncio에 대한 훌륭한 기사입니다. Asyncio는 동시에 여러 가지 일을하는 프로그램을 작성하기 때문에 많은 과장된 반응을 보이고 있습니다.

'yield from'의 구현은 특히 굉장합니다.

메모 : 나는 이것을 코멘트에 넣었을 것이지만 아직 거리가 없다. 어느 날 ....

관련 문제