Асинхронная загрузка файла на диск


У меня простой веб-сервер, написанный на Python с использованием aiohttp. Моя цель-это сервер, который может принимать множество файлов одновременно и поток их все на диск. Этот код работает, но я не уверен, если это эффективно, поддержка одновременных подключений:

import pathlib
import click
from aiohttp import web
from src import settings
from src import logger

log = None


async def upload_handler(request):
    """
    POST handler that accepts uploads to any path.
    The files are accepted and saved under the root path
    of the server.
    """
    # You cannot rely on Content-Length if transfer is chunked.
    size = 0
    local_path = settings.WWW_ROOT + request.path
    path_without_file = local_path.rsplit('/', 1)[0]
    pathlib.Path(path_without_file).mkdir(parents=True, exist_ok=True)

    with open(local_path, 'wb') as f:
        while True:
            chunk, is_end_of_http_chunk = await request.content.readchunk()
            if not chunk:
                break
            size += len(chunk)
            f.write(chunk)

    return web.Response(text='%d bytes uploaded' % size, status=201)


@click.command()
@click.option('--port', type=int, default=8000)
@click.option('--log-level', type=click.Choice(['debug', 'info', 'warning', 'error', 'critical']), default='info')
def cli(port, log_level):
    global log
    log = logger.get_default_logger(name='', log_level=log_level)
    app = web.Application(client_max_size=2e9, debug=settings.DEBUG)
    app.router.add_post('/{tail:.*}', upload_handler)
    web.run_app(app, port=port)


if __name__ == '__main__':
    cli()

В aiohttp материал чувствует себя немного магии для меня. Это выполняется на параллельных потоках? Я вижу высокую загрузку процессора в разы.

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



Комментарии
1 ответ

Интересные приложения.


Это выполняется на параллельных потоках?

Эммм, вроде. Но в ядре, а не в потоки питона. Предположим, у вас есть четыре ядра. То, что хорошо о ждать ... readchunk() заключается в том, что если в установившемся режиме потребляет 10% от основной, то питон может получить почти 40 вебсервер темы делать полезную работу для того, что многие клиенты до начала обмолота. (Веб-серверы открыты цикла, а не замкнутая система массового обслуживания цикла, так что вы почти всегда должны иметь небольшой запас, несколько холостых циклов, чтобы держаться подальше от патологических режимов производительности.)

    path_without_file = local_path.rsplit('/', 1)[0]

Я уверен, что это правильно, но это не очень идиоматические. Вам не нравится ОС.путь.каталог() ?

    pathlib.Path(path_without_file).mkdir(parents=True, exist_ok=True)

Ок, это немного безумно. Да, я понимаю, что nginx только руками авторизованные запросы. Но все-таки. Вы просто приняли произвольных байтов под контролем веб-клиент и используют его мутировать локальной файловой системе. Я рекомендую, по крайней мере, убедившись, что folder или path_without_file соответствует разумные выражения (не Юникод сочетания знаков). Же замечания, но больше так, для open(local_path, 'wb') фрагмент.

            f.write(chunk)

Вы не размещать какие-либо цифры времени, так что это не ясно, если входной сети и выходной диск является узким местом. Но если вы хотите идти дикой природы вы можете рассмотреть возможность отслеживания текущего смещения и делаю пуз написать на зачет, для получения большей глубины очереди против вашего местного RAID или другое хранилище. Когда жима, обязательно попробуйте много медленных клиентов, а также попробовать несколько тяжелых нападающих. Вы можете обнаружить, что выход tcpdump -r trace -ttt поучительно, для выявления палатках.

Замечания в сторону безопасности, престижность на какой-то хороший простой, надежный код. Похоже, он выполняет то, что вы намеревались сделать.

2
ответ дан 31 марта 2018 в 03:03 Источник Поделиться