Александр Кошелев: Препарирование работы...

28
ПРЕПАРИРОВАНИЕ РАБОТЫ АСИНХРОННОГО КОДА Александр Кошелев, Яндекс PyCon Россия, 2013

Upload: it-people

Post on 16-Jun-2015

849 views

Category:

Technology


1 download

DESCRIPTION

Что происходит внутри асинхронного кода? Как быть, когда логика становится cpu-bound? Можно ли сделать гибридную синхронно-асинхронную архитектуру? Я попробую ответить на эти вопросы на примере приложения на Tornado. Сделаю визуализацию работы приложения и предложу пути решения некоторых проблем.

TRANSCRIPT

Page 1: Александр Кошелев: Препарирование работы асинхронного кода

ПРЕПАРИРОВАНИЕ РАБОТЫАСИНХРОННОГО КОДА

Александр Кошелев, ЯндексPyCon Россия, 2013

Page 2: Александр Кошелев: Препарирование работы асинхронного кода

ЗАЧЕМAsynchronous is a new coolЭффективностьМного io-boundМало cpu-bound

Высокие нагрузкиМного клиентов

Page 3: Александр Кошелев: Препарирование работы асинхронного кода

КАКИнструменты на выбор

TwistedTornadoAsyncoreGevent...Node.js

Можно написать своё

Page 4: Александр Кошелев: Препарирование работы асинхронного кода

EVENT LOOPОпрос сокетовВызов хендлеров

Обработка таймаутовВызов колбеков

Page 5: Александр Кошелев: Препарирование работы асинхронного кода

EVENT LOOPwhile running: do_handle_sockets()

do_handle_timeouts()

do_handle_callbacks()

Page 6: Александр Кошелев: Препарирование работы асинхронного кода

ЭТО ЗНАЧИТВсе запросы обрабатываются в одном процессеОбработка одного запроса влияет на другиеТребуется изоляция контекста каждого запроса

Page 7: Александр Кошелев: Препарирование работы асинхронного кода

ГЛАВНЫЕ ПРАВИЛАНельзя блокироватьсяРазбивка на короткие этапыОбязательно io-bound

Page 8: Александр Кошелев: Препарирование работы асинхронного кода

ЭКСПЕРИМЕНТTornadocpu-boundОснован на реальной задаче

Page 9: Александр Кошелев: Препарирование работы асинхронного кода

ЭКСПЕРИМЕНТТЕСТОВОЕ ПРИЛОЖЕНИЕ

Принять запросПараллельно опросить бекэнды — принципиальноОбработать данныеОтдать ответ

Page 10: Александр Кошелев: Препарирование работы асинхронного кода

ЭКСПЕРИМЕНТТЕСТОВОЕ ПРИЛОЖЕНИЕ

Page 11: Александр Кошелев: Препарирование работы асинхронного кода

ЭКСПЕРИМЕНТХЕНДЛЕР

class AppHandler(object): @gen.engine def process(self, callback): yield gen.Task(self.do_io) self.do_cpu()

results = yield [gen.Task(self.do_branch), gen.Task(self.do_branch)] self.do_cpu()

yield gen.Task(self.do_io) self.do_cpu()

callback('ok')

Page 12: Александр Кошелев: Препарирование работы асинхронного кода

ЭКСПЕРИМЕНТХЕНДЛЕР

class AppHandler(object): # ...

def do_cpu(self, cycles=1000000): for _ in xrange(cycles): pass

@gen.engine def do_io(self, callback): yield gen.Task(self.http_client.fetch, 'http://localhost:5001/')

callback(None)

@gen.engine def do_branch(self, callback): yield [gen.Task(self.do_io) for _ in range(5)] self.do_cpu()

callback(None)

Page 13: Александр Кошелев: Препарирование работы асинхронного кода

ЭКСПЕРИМЕНТПРИЛОЖЕНИЕ

from tornado import web

from handler import AppHandler

class RootHandler(web.RequestHandler): @web.asynchronous def get(self): handler = AppHandler()

handler.process(self.return_response)

def return_response(self, response): self.finish(response)

application = web.Application([ (r'/', RootHandler),])

Page 14: Александр Кошелев: Препарирование работы асинхронного кода

1 ПАРАЛЛЕЛЬНЫЙ ЗАПРОС

Page 15: Александр Кошелев: Препарирование работы асинхронного кода

1 ПАРАЛЛЕЛЬНЫЙ ЗАПРОС

Page 16: Александр Кошелев: Препарирование работы асинхронного кода

N ПАРАЛЛЕЛЬНЫХ ЗАПРОСОВ

Page 17: Александр Кошелев: Препарирование работы асинхронного кода

3 ПАРАЛЛЕЛЬНЫХ ЗАПРОСА

Page 18: Александр Кошелев: Препарирование работы асинхронного кода

ВЫВОДЫЗапросы мешают друг другу из-за cpu-bound задачРастет время ответаЖадность

Page 19: Александр Кошелев: Препарирование работы асинхронного кода

WORKAROUNDFLASK + TORNADO IOLOOP

Синхронный воркерНе жадныйНо нужно несколько процессов

Event loop внутриПараллельный опрос бекэндов

Page 20: Александр Кошелев: Препарирование работы асинхронного кода

FLASK + TORNADO IOLOOPРЕАКТОР

def do(handler, args=(), kwargs={}): io_loop = tornado.ioloop.IOLoop.instance()

result = [None]

def callback(result_): result[0] = result_

io_loop.stop()

kwargs['callback'] = callback handler(*args, **kwargs)

io_loop.start()

return result[0]

Page 21: Александр Кошелев: Препарирование работы асинхронного кода

FLASK + TORNADO IOLOOPПРИЛОЖЕНИЕ

from flask import Flask

import handler, reactor

application = Flask(__name__)

@application.route('/')def root(): hndl = handler.AppHandler()

response = reactor.do(hndl.process)

return response

Page 22: Александр Кошелев: Препарирование работы асинхронного кода

1 ПАРАЛЛЕЛЬНЫЙ ЗАПРОС

Page 23: Александр Кошелев: Препарирование работы асинхронного кода

1 ПАРАЛЛЕЛЬНЫЙ ЗАПРОС

Page 24: Александр Кошелев: Препарирование работы асинхронного кода

N ПАРАЛЛЕЛЬНЫХ ЗАПРОСОВ

Page 25: Александр Кошелев: Препарирование работы асинхронного кода

3 ПАРАЛЛЕЛЬНЫХ ЗАПРОСА

Page 26: Александр Кошелев: Препарирование работы асинхронного кода

ИТОГИВремя не зависит от числа параллельных запросовБолее предсказуемое поведение

Page 27: Александр Кошелев: Препарирование работы асинхронного кода

ВЫВОДЫБаланс cpu-bound/io-boundВсё зависит от задачи

Page 28: Александр Кошелев: Препарирование работы асинхронного кода

ВОПРОСЫ?Спасибо

Александр Кошелев, Яндекс