Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Примеры применения asyncio: HTTPServer?!

Anna | 15.06.2014 | нет комментариев
Не так давным-давно зарелизилась новая версия Python 3.4 в changelog которой вошло много «вкусностей». Одна из таких — модуль asyncio, содержащий инфраструктуру пригодную для написания асинхронных сетевых приложений. Вследствие доктрины сопрограмм (coroutines), код асинхронного приложения примитивен для понимания и поддержки.

В статье на примере простого TCP (Echo) сервера я постараюсь показать с чем едят asyncio, и рискну устранить «неизбежный недочет» этого модуля, а именно неимение реализации асинхронного HTTP сервера.

INTRO

Прямой соперник и «брат» — это фреймворк tornado, тот, что отлично зарекомендовал себя и пользуется снисканной популярностью. Впрочем на мой взор, asyncore выглядит проще, больше логичен и продуман. Однако это не восхитительно, чай мы имеем дело со стандартной библиотекой языка.

Вы скажете, что на Python дозволено было и прежде писать асинхронные сервисы и будете правы. Но для этого требовались сторонние библиотеки и/или применения callback жанра программирования. Доктрина coroutine доведенная в этой версии Python фактически до совершенства разрешает писать линейный асинхронный код использую лишь вероятности стандартных библиотек языка.

Сразу хочу оговориться, что все это я писал под Linux, впрочем все используемые компоненты кроссплатформенные и под Windows тоже должно заработать. Но версия Python 3.4 непременна.

EchoServer

Пример Echo сервера есть в стандартной документации, но относится это к low-level API «Transports and protocols». Для «повседневного» применения рекомендуется high-level API «Streams». Пример кода TCP сервера в нем отсутствует, впрочем изучив пример из low-level API и посмотрев исходники того и иного модуля, написать примитивный TCP сервер не составляет труда.

import asyncio
import logging
import concurrent.futures

@asyncio.coroutine
def handle_connection(reader, writer):
    peername = writer.get_extra_info('peername')
    logging.info('Accepted connection from {}'.format(peername))
    while True:
        try:
            data = yield from asyncio.wait_for(reader.readline(), timeout=10.0)
            if data: 
                writer.write(data)
            else:
                logging.info('Connection from {} closed by peer'.format(peername))
                break
        except concurrent.futures.TimeoutError:
            logging.info('Connection from {} closed by timeout'.format(peername))
            break
    writer.close()

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    logging.basicConfig(level=logging.INFO)
    server_gen = asyncio.start_server(handle_connection, port=2007)
    server = loop.run_until_complete(server_gen)
    logging.info('Listening established on {0}'.format(server.sockets[0].getsockname()))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass # Press Ctrl C to stop
    finally:
        server.close()
        loop.close()

Все довольно видимо, но есть пара нюансов на которые стоит обратить внимание.

    server_gen = asyncio.start_server(handle_connection, port=2007)
    server = loop.run_until_complete(server_gen)

В первой строке создается не сам сервер, а генератор, тот, что при первом обращении к нему и недр asyncioсоздает и инициализирует по заданным параметрам TCP сервер. Вторая строка и есть пример такого обращения.

        try:
            data = yield from asyncio.wait_for(reader.readline(), timeout=10.0)
            if data: 
                writer.write(data)
            else:
                logging.info('Connection from {} closed by peer'.format(peername))
                break
        except concurrent.futures.TimeoutError:
            logging.info('Connection from {} closed by timeout'.format(peername))
            break

Функция-coroutine reader.readline() изготавливает асинхронное чтение данных из входного потока. Но ожидание данных для чтения не ограниченно по времени, если необходимо его перестать по таймауту нужно обернуть вызов функции-coroutine в asyncio.wait_for(). В этом случае по истечению заданного в секундах промежутка времени будет поднято исключение concurrent.futures.TimeoutError, которое дозволено обработать нужным образом.
Проверка что reader.readline() возвращает не пустое значение в данном приме<filename>’) def server_static(filename): return static_file(filename, root=os.path.join(root,’files’)) class AquaServer(bottle.ServerAdapter): “”"Bottle server adapter”"” def run(self, handler): import asyncio import logging from aqua.wsgiserver import WSGIServer logging.basicConfig(level=logging.ERROR) loop = asyncio.get_event_loop() server = WSGIServer(handler, loop=loop) server.bind(self.host, self.port) try: loop.run_forever() except KeyboardInterrupt: pass # Press Ctrl C to stop finally: server.unbindAll() loop.close() if __name__ == ‘__main__’: bottle.run(server=AquaServer, port=5000)

При написании кода WSGI сервера, я не подметил каких-то нюансов, которые дозволено бы было отнести на счет модуля asyncio. Исключительный момент, это специфика браузеров (скажем хрома), сбрасывать запрос если он видит что начинает получать огромный файл. Видимо это сделано с целью переключения на больше оптимизированный метод загрузки крупных файлов, потому как следом запрос повторяется и файл начинает приниматься штатно. Но 1-й сброшенный запрос вызывает исключение ConnectionResetError, если отдача файла по нему теснее началась с поддержка вызова функции StreamWriter.write(). Данный случай нужно обрабатывать и закрывать соединение с поддержкой StreamWriter.close().

Продуктивность

Для сравнительного теста я предпочел утилиту siege. В качестве подопытных выступили «наш пациент» (он же aqua :) в связке сbottle, довольно знаменитый Waitress WSGI сервер тоже в связке с bottle и безусловно же Tornado. В качестве приложения был минимально допустимый helloword. Тесты проводил со следующими параметрами: 100 и 1000 одновременных подключений; продолжительность теста 10 секунд; три варианта размера отдаваемых данных соответственно 13 байт, 13 килобайт и 13 мегабайт. Ниже итог:

100
concurent users
13 b 13 Kb 13 Mb
Avail. Trans/sec Avail. Trans/sec Avail. Trans/sec
aqua bottle 100,0% 835,24 100,0% 804,49 100,0% 23,66
waitress bootle 100,0% 707,24 100,0% 642,03 100,0% 1,76
tornado 100,0% 2282,45 100,0% 2071,27 100,0% 13,06
1000
concurent users
13 b 13 Kb 13 Mb
Avail. Trans/sec Avail. Trans/sec Avail. Trans/sec
aqua bottle 99,9% 800,41 99,9% 777,15 100,0% 25,22
waitress bootle 94,9% 689,23 99,9% 621,03 100,0% 4,37
tornado 100,0% 1239,88 100,0% 978,73 100,0% 7,61

Ну что сказать? Tornado безусловно рулит, но «наш пациент» кажется вырывается вперед на крупных файлах и усовершенствовал относительные показатели на большем числе соединений. К тому же он уверено обошел waitress (с его четырьмя дочерними процессами по числу ядер), тот, что не на плохом счету среди разработчиков. Не скажу что моё тестирование адекватно на 100%, но как оценочное вероятно сгодиться.

Ниже лог еще четырех больше жестких тестов. 100 и 1000 подключений, 13Mb тело, 60 секунд длительность. Тот, что так же показывает, что с крупными файлами сервер под asyncio работает увереннее.

Посмотреть (по портам 5000 aqua; 5002 tornado)

$ siege -c 100 -b -t 60S http://127.0.0.1:5000/
** SIEGE 2.70
** Preparing 100 concurrent users for battle.
The server is now under siege...
Lifting the server siege...      done.
Transactions:               1556 hits
Availability:              99.87 %
Elapsed time:              59.21 secs
Data transferred:       20228.00 MB
Response time:              3.69 secs
Transaction rate:          26.28 trans/sec
Throughput:           341.63 MB/sec
Concurrency:               96.87
Successful transactions:        1556
Failed transactions:               2
Longest transaction:            4.53
Shortest transaction:           1.15

$ siege -c 1000 -b -t 60S http://127.0.0.1:5000/
** SIEGE 2.70
** Preparing 1000 concurrent users for battle.
Transactions:               1570 hits
Availability:              60.18 %
Elapsed time:              59.84 secs
Data transferred:       20410.00 MB
Response time:              5.56 secs
Transaction rate:          26.24 trans/sec
Throughput:           341.08 MB/sec
Concurrency:              145.80
Successful transactions:        1570
Failed transactions:            1039
Longest transaction:           20.44
Shortest transaction:           0.00

$ siege -c 100 -b -t 60S http://127.0.0.1:5002/
** SIEGE 2.70
** Preparing 100 concurrent users for battle.
The server is now under siege...
Lifting the server siege...      done.
Transactions:                942 hits
Availability:             100.00 %
Elapsed time:              59.69 secs
Data transferred:       12246.00 MB
Response time:              5.97 secs
Transaction rate:          15.78 trans/sec
Throughput:           205.16 MB/sec
Concurrency:               94.25
Successful transactions:         942
Failed transactions:               0
Longest transaction:           14.82
Shortest transaction:           0.57

$ siege -c 1000 -b -t 60S http://127.0.0.1:5002/
** SIEGE 2.70
** Preparing 1000 concurrent users for battle.
The server is now under siege...
Lifting the server siege...      done.
Transactions:                857 hits
Availability:              55.65 %
Elapsed time:              59.07 secs
Data transferred:       11141.00 MB
Response time:             20.14 secs
Transaction rate:          14.51 trans/sec
Throughput:           188.61 MB/sec
Concurrency:              292.16
Successful transactions:         857
Failed transactions:             683
Longest transaction:           51.19
Shortest transaction:           3.26

OUTRO

Асинхронный вебсервер с применением asyncio имеет право на жизнь. Допустимо говорить об применении таких серверов в серьезных планах пока рано, но позже тестирования, обкатки и с возникновением асинхронных драйверов asyncio к базам данных и key-value хранилищам — это абсолютно может быть допустимо.

Источник: programmingmaster.ru

Оставить комментарий
БАЗА ЗНАНИЙ
СЛУЧАЙНАЯ СТАТЬЯ
СЛУЧАЙНЫЙ БЛОГ
СЛУЧАЙНЫЙ МОД
СЛУЧАЙНЫЙ СКИН
НОВЫЕ МОДЫ
НОВЫЕ СКИНЫ
НАКОПЛЕННЫЙ ОПЫТ
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB