почему время увеличивается для более чем одного запроса к серверу asyncio в python? - PullRequest
0 голосов
/ 04 июля 2018

Я написал питонический сервер с сокетом. который должен получать запросы одновременно (параллельно) и отвечать на них параллельно. Когда я отправляю более одного запроса, время ответа увеличивается больше, чем я ожидал.

Сервер:

import datetime
import asyncio, timeit
import json, traceback
from asyncio import get_event_loop

requestslist = []
loop = asyncio.get_event_loop()

async def handleData(reader, writer):
    message = ''
    clientip = ''
    data = bytearray()
    print("Async HandleData", datetime.datetime.utcnow())


    try:
        start = timeit.default_timer()
        data = await reader.readuntil(separator=b'\r\n\r\n')
        msg = data.decode(encoding='utf-8')
        len_csharp_message = int(msg[msg.find('content-length:') + 15:msg.find(';dmnid'):])
        data = await reader.read(len_csharp_message)
        message = data.decode(encoding='utf-8')

        clientip = reader._transport._extra['peername'][0]
        clientport = reader._transport._extra['peername'][1]
        print('\nData Received from:', clientip, ':', clientport)
        if (clientip, message) in requestslist:
            reader._transport._sock.close()

        else:
            requestslist.append((clientip, message))

            # adapter_result = parallel_members(message_dict, service, dmnid)
            adapter_result = '''[{"name": {"data": "data", "type": "str"}}]'''
            body = json.dumps(adapter_result, ensure_ascii=False)
            print(body)

            contentlen = len(bytes(str(body), 'utf-8'))
            header = bytes('Content-Length:{}'.format(contentlen), 'utf-8')
            result = header + bytes('\r\n\r\n{', 'utf-8') + body + bytes('}', 'utf-8')
            stop = timeit.default_timer()
            print('total_time:', stop - start)
            writer.write(result)
            writer.close()
        writer.close()
        # del writer
    except Exception as ex:
        writer.close()
        print(traceback.format_exc())
    finally:
        try:
            requestslist.remove((clientip, message))
        except:
            pass


def main(*args):
    print("ready")
    loop = get_event_loop()
    coro = asyncio.start_server(handleData, 'localhost', 4040, loop=loop, limit=204800000)
    srv = loop.run_until_complete(coro)
    loop.run_forever()


if __name__ == '__main__':
    main()

Когда я отправляю один запрос, он торгует 0,016 сек. но для большего запроса, это время увеличится.

информация о процессоре: intel xeon x5650

клиент:

import multiprocessing, subprocess
import time
from joblib import Parallel, delayed


def worker(file):
    subprocess.Popen(file, shell=False)


def call_parallel (index):
    print('begin ' , index)
    p = multiprocessing.Process(target=worker(index))
    p.start()
    print('end ' , index)

path = r'python "/test-Client.py"'     # ## client address
files = [path, path, path, path, path, path, path, path, path, path, path, path]
Parallel(n_jobs=-1, backend="threading")(delayed(call_parallel)(i) for index,i  in  enumerate(files))

для этого клиента, который отправляет 12 запросов синхронно, общее время для каждого запроса составляет 0,15 с.

Я ожидаю, что для любого количества запросов время будет фиксированным.

1 Ответ

0 голосов
/ 04 июля 2018

Что такое запрос

Один запрос (грубо говоря) состоит из следующих шагов:

  1. запись данных в сеть
  2. тратить время на ожидание ответа
  3. читать ответ из сети

№1 / №3 обрабатывается вашим процессором очень быстро. Шаг №2 - это байтовое путешествие от вашего компьютера к какому-либо серверу (например, в другом городе) и обратно по проводам: обычно это занимает гораздо больше времени.

Как работают асинхронные запросы

Асинхронные запросы на самом деле не являются «параллельными» с точки зрения обработки: это все же ваше единственное ядро ​​ЦП, которое может обрабатывать одну вещь за раз. Но выполнение нескольких асинхронных запросов позволяет вам использовать шаг №2 одного запроса для выполнения шагов №1 / №3 другого запроса вместо того, чтобы тратить огромное количество времени. По этой причине несколько асинхронных запросов обычно заканчиваются раньше, чем одинаковые.

Запуск асинхронного кода без задержки в сети

Но когда вы запускаете вещи локально, шаг №2 не занимает много времени: ваш компьютер и сервер - это одно и то же, и байты не отправляются в путешествие по сети. Просто нет времени, которое можно использовать на шаге №2 для запуска нового запроса. Только одно ядро ​​процессора работает, обрабатывая одну вещь одновременно.

Вы должны проверить запросы к серверу, который отвечает с некоторой задержкой, чтобы увидеть ожидаемые результаты.

...