реклама
Бургер менюБургер меню

Кеннет Рейтц – Автостопом по Python (страница 61)

18

Элай Бендерски (Eli Bendersky), один из основных разработчиков Python, написал статью, посвященную вопросу снижения количества копий одних и тех же данных, хранящихся в памяти, с помощью буферов памяти (http://tinyurl.com/bendersky-buffer-protocol). Используя этот прием, вы даже можете считать данные из файла или сокета и поместить их в существующий буфер. Для получения более подробной информации обратитесь к документации для буферов протоколов и PEP 3118 (https://docs.python.org/3/c-api/buffer.html), где предлагаются улучшения, которые были реализованы в Python 3 и обратно портированы для версий Python 2.6 и выше.

Распределенные системы

Распределенные вычислительные системы выполняют задачу сообща (вроде игр, чат-комнат в Интернете или расчетов Hadoop) путем передачи информации друг другу.

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

Работа с сетью

В Python коммуникация для соединенных сетей зачастую обрабатывается с помощью асинхронных инструментов или потоков, что позволяет обойти ограничение в один поток, создаваемое глобальной блокировкой интерпретатора. Все библиотеки, перечисленные в табл. 9.1, решают одну и ту же проблему — обходят GIL — с помощью разной функциональности.

Производительность сетевых инструментов из стандартной библиотеки Python

Инструмент asyncio (https://docs.python.org/3/library/asyncio.html) был представлен в Python 3.4. Включает в себя идеи, почерпнутые у сообществ разработчиков вроде тех, что поддерживают библиотеки Twisted и gevent. Это инструмент для работы с конкуренцией, а самым частым приложением конкуренции являются сетевые сервера. В документации к asyncore (предшественнике asyncio) говорится следующее:

Существует лишь два способа заставить программу, работающую на одном процессоре, выполнять «больше одной задачи одновременно». Многопоточное программирование — самый простой и популярный способ сделать это, но существует еще один прием, который позволяет воспользоваться практически всеми преимуществами многопоточности, не задействуя на самом деле более одного потока. Применять этот прием имеет смысл, только если ваша программа ограничена по вводу/выводу. Если программа ограничена по процессору, то заранее запланированные потоки — это, возможно, именно то, что вам нужно. Однако сетевые сервера редко бывают ограниченными по процессору.

asyncio все еще находится в стандартной библиотеке Python на временной основе — ее API может измениться и потерять обратную совместимость, поэтому сильно не привыкайте.

Не вся функциональность нова — asyncore (объявлена устаревшей в Python 3.4) имеет цикл событий, асинхронные сокеты[105] и асинхронный ввод/вывод информации из файлов, а asynchat (также объявлена устаревшей в Python 3.4) имеет асинхронные очереди[106]. В asyncio добавлен один важный элемент — формализованная реализация сопрограмм. В Python это формально определяется как функция сопрограммы, то есть функция, чье описание начинается с конструкции async def, а не просто с def (если используется старый синтаксис, то применяется декоратор @asyncio.coroutine), и как объект, получаемый путем вызова функции сопрограммы (некого рода вычислений или операций ввода/вывода). Сопрограмма может обращаться к процессору и получить возможность участвовать в асинхронном цикле событий по очереди вместе с другими сопрограммами.

Множество страниц документации посвящено примерам, помогающим сообществу, поскольку такая концепция для языка новая. Она прозрачна, продуманна, и на нее определенно стоит обратить внимание. В этой интерактивной сессии мы просто хотим показать функции для цикла событий и некоторые доступные классы:

>>> import asyncio

>>>

>>> [l for l in asyncio.__all__ if 'loop' in l]

['get_event_loop_policy', 'set_event_loop_policy',

'get_event_loop', 'set_event_loop', 'new_event_loop']

>>>

>>> [t for t in asyncio.__all__ if t.endswith('Transport')]

['BaseTransport', 'ReadTransport', 'WriteTransport', 'Transport',

'DatagramTransport', 'SubprocessTransport']

>>>

>>> [p for p in asyncio.__all__ if p.endswith('Protocol')]

['BaseProtocol', 'Protocol', 'DatagramProtocol',

'SubprocessProtocol', 'StreamReaderProtocol']

>>>

>>> [q for q in asyncio.__all__ if 'Queue' in q]

['Queue', 'PriorityQueue', 'LifoQueue', 'JoinableQueue',

'QueueFull', 'QueueEmpty']

gevent

gevent (http://www.gevent.org/) — это библиотека Python для работы с сетью, основанная на сопрограммах. Использует гринлеты, чтобы предоставить высокоуровневый синхронный API на базе цикла событий библиотеки libev (http://software.schmorp.de/pkg/libev.html), написанной на С. Гринлеты основаны на библиотеке greenlet (http://greenlet.readthedocs.io/en/latest/) — миниатюрные зеленые потоки (https://en.wikipedia.org/wiki/Green_threads) (или потоки уровня пользователя, по смыслу противоположные потоками, управляемым ядром), которые разработчик может свободно заморозить, переключаясь между гринлетами. Если хотите получить более подробную информацию, обратите внимание на семинар Кавьи Джоши (Kavya Joshi) A Tale of Concurrency Through Creativity in Python (http://bit.ly/kavya-joshi-seminar).

Многие пользуются gevent, поскольку она легковесна и тесно связана с лежащей в ее основе библиотекой libev, написанной на С, что повышает производительность. Если вам нравится идея интеграции асинхронного ввода/вывода и гринлетов, эта библиотека отлично вам подойдет. Установите ее с помощью pip:

$ pip install gevent

Рассмотрим пример из документации к greenlet:

>>> import gevent

>>>

>>> from gevent import socket

>>> urls = ['www.google.com', 'www.example.com', 'www.python.org']

>>> jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls]

>>> gevent.joinall(jobs, timeout=2)

>>> [job.value for job in jobs]

['74.125.79.106', '208.77.188.166', '82.94.164.162']

В документации содержится множество других примеров.

Twisted

Twisted (http://twistedmatrix.com/trac/) — это управляемый событиями движок для работы с сетями. Он может применяться для создания приложений на основе разных сетевых протоколов, включая серверы и клиенты HTTP, а также приложений, использующих протоколы SMTP, POP3, IMAP или SSH, протоколы мгновенного обмена сообщениями, и многих других (http://twistedmatrix.com/trac/wiki/Documentation). Установите его с помощью команды pip:

$ pip install twisted

Twisted существует с 2002 года и имеет верное сообщество. Ее можно назвать Emacs среди библиотек сопрограмм: все функции встроены (поскольку функциональность должна быть асинхронной для того, чтобы вы могли работать). Возможно, наиболее полезными инструментами являются асинхронная оболочка для соединений с базой данных (расположен в twisted.enterprise.adbapi), DNS-сервер (в twisted.names), прямой доступ к пакетам (в twisted.pair) и дополнительные протоколы вроде AMP, GPS и SOCKSv4 (в twisted.protocols). Большая часть функциональности Twisted работает и в Python 3. Когда вы вызываете команду pip install в среде Python 3, вы получаете все библиотеки, портированные к этому моменту. Если вы нашли то, что вам нужно, в API (http://twistedmatrix.com/documents/current/api/moduleIndex.html), которого нет в вашей версии Twisted, вам стоит воспользоваться Python 2.7.

Для получения более подробной информации см. книгу Twisted (издательство O’Reilly) Джессики МакКеллар (Jessica McKellar) и Эйба Феттига (Abe Fettig). В дополнение к ней по адресу http://twistedmatrix.com/documents/current/core/examples/ приводится более 42 примеров использования Twisted, а в этом показываются их недавние достижения в скорости (http://speed.twistedmatrix.com/).

PyZMQ

PyZMQ (http://zeromq.github.com/pyzmq/) — это привязка к Python для ZeroMQ (http://www.zeromq.org/). Вы можете установить ее с помощью команды pip:

$ pip install pyzmq

ØMQ (также записывается как ZeroMQ, 0MQ или ZMQ) — библиотека для обмена сообщениями, которая имеет API, похожий на API сокетов. Предназначена для использования в масштабируемых распределенных или одновременно выполняемых приложениях. По сути, она реализует асинхронные сокеты и очереди, а также предоставляет пользовательский список «типов» сокетов, которые определяют, как работает ввод/вывод для каждого сокета. Рассмотрим пример:

Тип сокета zmq.REP соответствует ее парадигме «запрос-ответ».

Как и в случае обычных сокетов, вы привязываете сервер к IP-адресу и порту.

Клиент имеет тип zmq.REQ — ZMQ определяет следующие константы: zmq.REQ, zmq.REP, zmq.PUB, zmq.SUB, zmq.PUSH, zmq.PULL, zmq.PAIR. Они устанавливают порядок отправки и принятия данных сокетом.

Как и обычно, клиент соединяется с IP-адресом и портом, привязанным к серверу.

Эта реализация выглядит и «крякает» как сокеты, улучшенные с помощью добавления очередей и разнообразных шаблонов ввода/вывода.

Идея использования шаблонов заключается в том, чтобы предоставить строительный материал для распределенной сети.

Сокеты имеют следующие основные шаблоны.

• Запрос — ответ. zmq.REQ и zmq.REP соединяют набор клиентов с набором сервисов. Это может использоваться для создания шаблонов удаленного вызова процедуры или распределения задач.