Async Python

34
Асинхронное программирование в Python

Transcript of Async Python

Page 1: Async Python

Асинхронное программирование в

Python

Page 2: Async Python

Проблема C10K

• Условное название задачи конфигурирования и обслуживания высокопроизводительного сервера

• Сервер должен быть способен обслуживать порядка 10 тыс. соединений одновременно

Page 3: Async Python

Проблема C10K

• Решение №1

• Поток на каждого клиента

• Решение №2

• Поток на несколько клиентов

Page 4: Async Python

Сервера, выдерживающие проблему

• nginx, which relies on an event-driven (asynchronous) architecture, instead of threads, to handle requests

• Lighttpd, which relies on an asynchronous architecture to handle requests

• Tornado, a non-blocking web server and web application framework written in Python (used by Facebook's FriendFeed)

• Apache AWF (retired, formerly Apache Deft), asynchronous, non-blocking web server running on the JVM

• JBoss Netty, a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients

• Node.js, asynchronous, non-blocking web server running on Google's V8 JavaScript engine

Page 5: Async Python

Сервера, выдерживающие проблему

• EventMachine, an asynchronous, non-blocking web server running on Ruby EventMachine

• Cowboy (web server), another very lightweight web server written in Erlang

• asyncore (in the standard Python library), a non-blocking web server library. It is based on Medusa, which is no longer maintained.

• Django, some research is being done using the asynchronous IO support in Python 3.3

Page 6: Async Python

sync VS async

Page 7: Async Python

Sync• Делится на однопоточную и многопоточную.

Page 8: Async Python

Sync: 1 thread• В однопоточной модели каждая задача выполняется последовательно. Поток выполняет следующую задачу только тогда, когда предыдущая точно завершилась.

Page 9: Async Python

Sync: N threads• В многопоточной модели можно достичь некоторой "одновременной работы" задач, но на каждую задачу выделяется один поток. Деталями управления в многопоточной синхронной модели управляет ОС.

Page 10: Async Python

Async• Асинхронная модель всегда будет чередовать выполнение задач. Взаимодействие задач (их синхронизация) лежит на ответственности программиста.

Page 11: Async Python

sync VS async• По сравнению с синхронной моделью, асинхронная модель лучше когда:

1. Когда есть много задач таких, что почти всегда есть по меньшей мере одна задача, которая может выполняться.

2. Задачи выполняют много операций ввода-вывода, приводя к тому, что синхронная задача проводит много времени впустую, блокируясь, в то время когда другие задачи могли бы выполняться.

3. Задачи независимы друг от друга и не нуждаются во взаимодействии.

Page 12: Async Python

Event-driven programming

• Событийно-ориентированное программирование

• Способ построения программы

• В коде явным образом выделяется главный цикл приложения

• Тело главного цикла состоит из двух частей: выборки события и обработки события.

Page 13: Async Python

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

• При возникновении задачи, он немедленно реагирует.

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

будем блокировать наш внешний цикл, который как раз и следит за приходом новых задач или

выполнением следующих

Page 14: Async Python

Async-servers in Python• Name Lic. Doc Ex. Prod. Com. Act. Blog Twt Rep. Pool Wsgi Scket Cmet Epoll Test Style

• Twisted! MIT! Yes! Yes! Yes! Huge! Yes! Lots! No! Trac! Yes! Yes! Yes! No! Yes! Yes! Callback!

• Tornado! Apache! Yes! Yes! F.Feed!Yes! Yes! FB! Yes! GHub!No! Lim.! Yes! No! Yes! No!Async!

• Orbited MIT Yes Yes Yes Yes Yes Yes No Trac No No Yes Yes Yes Yes Callback

• DieselWeb BSD Yes Yes STalk Yes Yes Yes Yes BitB. No Lim. Yes Yes Yes No Generator

• MultiTask MIT Some No No No No Yes No Bzr No No No No No No Generator

• Chiral GPL2 API No No IRC No No No Trac No Yes Yes Yes Yes Yes Coroutine

• Eventlet! MIT! Yes! Yes! S. Life! Yes! Yes! Yes! No! BitB.! Yes! Yes! Yes! No! Yes! Yes! Greenlet!

• FriendlyFlow GPL2 Some One No No No No Yes Ggle No No Yes No No Yes Generator

• Weightless GPL2 Yes No Yes No No No Yes SF No No Yes No No Yes Generator

• Fibra MIT No No No No No Yes No Ggle No No Yes No No No Generator

• Concurrence! MIT! Yes! Yes! hyves!Yes! Yes! No! No! GHub!No! Yes! Yes! No! Yes! Yes! Tasklet!

• Circuits MIT Yes Yes Yes Yes Yes Yes Yes Trac No Yes Yes No No Yes Async

http://nichol.as/asynchronous-servers-in-python

Page 15: Async Python

Async-servers in Python• What License does the framework have?

• Does it provide documentation?

• Does the documentation contain examples?

• Is it used in production somewhere?

• Does it have some sort of community (mailinglist, irc, etc..)?

• Is there any recent activity?

• Does it have a blog (from the owner)?

• Does it have a twitter account?

• Where can i find the repository?

• Does it have a Thread Pool?

• Does it provide access to a TCP Socket?

• Does it have any Comet features?

• Is it using EPOLL?

• What kind of server is it? (greenlets, callbacks, generators etc..)

http://nichol.as/asynchronous-servers-in-python

Page 16: Async Python

Tornado

• Расширяемый, неблокирующий веб-сервер и фреймворк, написанный на Python.

• Создан для использования в проекте FriendFeed; компания была приобретена Facebook в 2009 году и после этого были открыты исходные коды Tornado.

Page 17: Async Python

Tornadoimport tornado.httpserver import tornado.ioloop import tornado.options import tornado.web import logging from tornado.options import define, options !define("port", default=8888, help="run on the given port", type=int) !class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world!") !def main(): tornado.options.parse_command_line() application = tornado.web.Application([ (r"/", MainHandler), ]) http_server = tornado.httpserver.HTTPServer(application) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start() !if __name__ == "__main__": main()

http://programmingzen.com/2009/09/13/benchmarking-tornado-vs-twisted-web-vs-tornado-on-twisted/

Page 18: Async Python

Twisted• Проекты на Twisted могут поддерживать TCP,

UDP, SSL/TLS, IP Multicast, Unix domain sockets, большое количество протоколов, включая HTTP, XMPP, NNTP, IMAP, SSH, IRC, FTP и другие.

• Основан на парадигме событийно-ориентированного программирования, и это значит, что пользователи Twisted пишут небольшие программы обратного вызова, которые вызываются фреймворком.

Page 19: Async Python

Twistedfrom twisted.internet import epollreactor epollreactor.install() from twisted.internet import reactor from twisted.web import server, resource !!class Simple(resource.Resource): isLeaf = True def render_GET(self, request): return "Hello, world!" !!site = server.Site(Simple()) reactor.listenTCP(8888, site) reactor.run()

http://programmingzen.com/2009/09/13/benchmarking-tornado-vs-twisted-web-vs-tornado-on-twisted/

Page 20: Async Python

Чуть больше о Twisted

Page 21: Async Python

Twisted• Шаблон реактор - однопоточный. • Twisted сам реализует цикл реактора, мы сами не должны его реализовывать.

• При написании кода, нужно продумывать основную логику задачи.

• Цикл реактора вызывает наш код в тот момент, когда мы об этом сами скажем. Реактор наперед не может знать, какую часть кода нужно вызвать.

• Когда наши callback’и выполняются, цикл реактора не выполняется, и наоборот.

• После возврата из callback'а, цикл реактора возобновляется

Page 22: Async Python

Что делать с исключениями?

Page 23: Async Python

Errbacks

• Реактор не обрабатывает исключения, потому что ничего о них не знает.

• Реактор -- низкоуровневый метод, а исключения генерируются из более высокоуровневого кода.

Page 24: Async Python

Sync VS Async1. Синхронный код:!try:! text = run_print()!except Exception, err:! print err! sys.exit()!else:! print text! sys.exit()!!!2. Асинхронный код:!def print_ok(text):! print text! reactor.stop()!!def print_fail(err):! print >>sys.stderr, err! reactor.stop()!!run_print(print_ok, print_err)!!reactor.run()

Page 25: Async Python

Deferredfrom twisted.internet.defer import Deferred!!def print_ok(text):! print text!!def print_fail(err):! print >>sys.stderr, err!!d = Deferred()!d.addCallbacks(print_ok, print_fail)!d.callback("Text is correct") !# или d.errback(Exception('I have failed.'))!print "End."!!reactor.run()

Page 26: Async Python

Deferred

Page 27: Async Python

Сервер на Twistedfrom twisted.internet import reactor!from twisted.internet.protocol import ServerFactory !from twisted.protocols.basic import LineOnlyReceiver !!class ChatProtocol(LineOnlyReceiver): !! name = "" ! def getName(self): ! if self.name!="": ! return self.name ! return self.transport.getPeer().host !! def connectionMade(self): ! print "New connection from "+self.getName() ! self.sendLine("Welcome to my my chat server.") ! self.sendLine("Send '/NAME [new name]' to change your name.") ! self.sendLine("Send '/EXIT' to quit.") ! self.factory.sendMessageToAllClients(self.getName()+" has joined the party.") ! self.factory.clientProtocols.append(self)

Page 28: Async Python

Сервер на Twisted!! def connectionLost(self, reason): ! print "Lost connection from "+self.getName() ! self.factory.clientProtocols.remove(self) ! self.factory.sendMessageToAllClients(self.getName()+" has disconnected.") !! def lineReceived(self, line): ! print self.getName()+" said "+line ! if line[:5]=="/NAME": ! oldName = self.getName() ! self.name = line[5:].strip() ! self.factory.sendMessageToAllClients(oldName+" changed name !! ! ! ! to "+self.getName()) ! elif line=="/EXIT": ! self.transport.loseConnection() ! else: ! self.factory.sendMessageToAllClients(self.getName()+" says "+line) !! def sendLine(self, line): ! self.transport.write(line+"\r\n") !!

Page 29: Async Python

Сервер на Twisted

class ChatProtocolFactory(ServerFactory): !! protocol = ChatProtocol !! def __init__(self): ! self.clientProtocols = [] !! def sendMessageToAllClients(self, mesg): ! for client in self.clientProtocols:! client.sendLine(mesg) !!print "Starting Server"!factory = ChatProtocolFactory()!reactor.listenTCP(12345, factory)!reactor.run()!

В качестве клиента: telnet localhost 12345

Page 30: Async Python

Async I/O for Python 3• Пишем асинхронный код без тредов и коллбеков

• Сопрограмма (англ. coroutine) — компонент программы, обобщающий понятие подпрограммы, который дополнительно поддерживает множество входных точек (а не одну как подпрограмма) и остановку и продолжение выполнения с сохранением определённого положения.

Page 31: Async Python

Yield from!!class BinaryTree_ForLoop:! def __init__(self, left=None, us=None, right=None):! self.left = left! self.us = us! self.right = right!! def __iter__(self):! if self.left:! for node in self.left:! yield node! if self.us:! yield self.us! if self.right:! for node in self.right:! yield node!

class BinaryTree:! def __init__(self, !left=None, us=None, right=None):! self.left = left! self.us = us! self.right = right!! def __iter__(self):! if self.left:! yield from self.left! if self.us:! yield self.us! if self.right:! yield from self.right!!

# For comparison, here is the same thing using for-loops!# instead of yield-from.

Page 32: Async Python

Tulip VS Twistedimport tulip!from tulip import [email protected]!def download(url):! response = yield from http.request('GET', url)! for k, v in response.items():! print('{}: {}'.format(k, v[:80]))!! data = yield from response.read()! print('\nReceived {} bytes.\n'.format(len(data)))!!if __name__ == '__main__':! loop = tulip.get_event_loop()! coroutine = download('http://omegafeihong.tumblr.com')! loop.run_until_complete(coroutine)

Page 33: Async Python

Tulip VS Twistedfrom twisted.internet import reactor!from twisted.internet.defer import Deferred, succeed!from twisted.internet.protocol import Protocol!from twisted.web.client import Agent!!def print_headers(response):! for k, v in response.headers.getAllRawHeaders():! print('{}: {}'.format(k, v[0][:80]))!! return get_response_body(response)!!def get_response_body(response):! class BodyReceiver(Protocol):! def dataReceived(self, data):! chunks.append(data)! def connectionLost(self, reason):! finished.callback(''.join(chunks))!! finished = Deferred()! chunks = []! response.deliverBody(BodyReceiver())! return finished!!def print_body(data):! print('\nReceived {} bytes.\n'.format(len(data)))! return succeed(None)!!

if __name__ == '__main__':! agent = Agent(reactor)! d = agent.request('GET', 'http://megafeihong.tumblr.com')! d.addCallback(print_headers)! d.addCallback(print_body)! d.addCallback(lambda x: reactor.stop())! reactor.run()

Page 34: Async Python

Почитать• http://ninaevseenko.github.io/async_twisted_ru/async_twisted_ru.pdf

• http://legacy.python.org/dev/peps/pep-3156/#abstract

• http://moscowdjango.ru/meetup/18/tulip/

• http://neednourishment.blogspot.ru

• http://www.ibm.com/developerworks/ru/library/l-python_part_10/index.html

• http://www.slideshare.net/Smirnov.Andrey/twisted-framework-python-2211313

• http://moscowdjango.ru/meetup/14/gil-and-python-why/

• http://legacy.python.org/dev/peps/pep-0380/

• http://legacy.python.org/dev/peps/pep-3156/#event-loop-classes

• http://ru.wikipedia.org/wiki/%D1%EE%E1%FB%F2%E8%E9%ED%EE-%EE%F0%E8%E5%ED%F2%E8%F0%EE%E2%E0%ED%ED%EE%E5_%EF%F0%EE%E3%F0%E0%EC%EC%E8%F0%EE%E2%E0%ED%E8%E5

• http://nichol.as/asynchronous-servers-in-python

• http://programmingzen.com/2009/09/13/benchmarking-tornado-vs-twisted-web-vs-tornado-on-twisted/

• http://www.slideshare.net/megafeihong/tulip-24190096

• http://www.dabeaz.com/coroutines/