SlideShare a Scribd company logo
asyncio для процессинга
распределенной базы данных
О себе
●
Никита Семенов
●
5 лет играл в покер
●
R, scikit-learn, data mining
●
1 год python backend в pickorban.com
pickorban.com
●
Machine learning
для Dota 2
О чем доклад
●
asyncio
●
aiohttp, web socket
●
gather, semaphore, asyncio.Queue
●
От плохого к хорошему
api.steampowered.com/IDOTA2Match_570/GetMatchDetails/V1/?match_id=...&key=...
Matches dbs Users dbsBlack Box
1 000 000 000 игр
1 тб сжатых данных
70 тб скачено через Dota Web API
10 000 000 игроков
500 гб данных
Key:value
+1 000 000 каждый день
GET /getMatch/{match_id}
GET /getNextMatch
Решение #1
sequential read:
Читаем все матчи подряд и апдейтим игроков
SSD 200 MBsec, HDD 100 MBsec
Решение #1
sequential read:
Читаем все матчи подряд и апдейтим игроков
import requests
from config import db_addresses
for db_server, db_port in db_addresses:
while not error:
r = requests.get('http://{}:{}/getNextMatch'.format(db_server,
db_port))
match_json = r.json()
process(match_json)
Как хочется?
●
Чтобы использовалось 100% CPU
Как хочется?
●
Чтобы использовалось 100% CPU
Решение
Task 1: In process
Task 2: In process
Task 3: Finish
Task 4: Canceled
from concurrent.futures import ThreadPoolExecutor
def fetch(server, port):
r = requests.get('http://{}:{}/getNextMatch'.format(server, port))
process(r.json())
with ThreadPoolExecutor(max_workers = 10) as executor:
for db_server, db_port in db_addresses:
while True:
for _ in range(10):
executor.submit(fetch, db_server, db_port)
sleep(0.015)
from concurrent.futures import ThreadPoolExecutor
def fetch(server, port):
r = requests.get('http://{}:{}/getNextMatch'.format(server, port))
process(r.json())
with ThreadPoolExecutor(max_workers = 10) as executor:
for db_server, db_port in db_addresses:
while True:
for _ in range(10):
executor.submit(fetch, db_server, db_port)
sleep(0.015)
Накрасивый вызов аргументов
from concurrent.futures import ThreadPoolExecutor
def fetch(server, port):
r = requests.get('http://{}:{}/getNextMatch'.format(server, port))
process(r.json())
with ThreadPoolExecutor(max_workers = 10) as executor:
for db_server, db_port in db_addresses:
while True:
for _ in range(10):
executor.submit(fetch, db_server, db_port)
sleep(0.015)
Параметры придется подбирать эксперементально
from concurrent.futures import ThreadPoolExecutor
def fetch(server, port):
r = requests.get('http://{}:{}/getNextMatch'.format(server, port))
process(r.json())
with ThreadPoolExecutor(max_workers = 10) as executor:
for db_server, db_port in db_addresses:
while True:
for _ in range(10):
executor.submit(fetch, db_server, db_port)
sleep(0.015)
Треды не бесконечны и затрагивают OS
from concurrent.futures import ThreadPoolExecutor
def fetch(server, port):
r = requests.get('http://{}:{}/getNextMatch'.format(server, port))
process(r.json())
with ThreadPoolExecutor(max_workers = 10) as executor:
for db_server, db_port in db_addresses:
while True:
for _ in range(10):
executor.submit(fetch, db_server, db_port)
sleep(0.015)
Неявное отличие блокирующих функций от неблокирующих
import asyncio
import aiohttp
async def fetch(session, url):
async with session.get(url) as response:
match_json = await response.json()
process(match_json)
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
for _ in range(10):
loop.create_task(fetch(session, url))
await asyncio.sleep(0.015)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
import asyncio
import aiohttp
async def fetch(session, url):
async with session.get(url) as response:
match_json = await response.json()
process(match_json)
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
for _ in range(10):
loop.create_task(fetch(session, url))
await asyncio.sleep(0.015)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
1 скрипт = 1 loop, 1 thread
import asyncio
import aiohttp
async def fetch(session, url):
async with session.get(url) as response:
match_json = await response.json()
process(match_json)
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
for _ in range(10):
loop.create_task(fetch(session, url))
await asyncio.sleep(0.015)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Все еще проблема со sleep и синтаксисом
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
tasks = []
for _ in range(10):
tasks.append(fetch(session, url))
await asyncio.gather(*tasks)
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
tasks = []
for _ in range(10):
tasks.append(fetch(session, url))
await asyncio.gather(*tasks)
Gather работает со скоростью самого медленного запроса
sem = asyncio.Semaphore(10)
async def fetch(session, url):
async with session.get(url) as response:
match_json = await response.json()
process(match_json)
sem.release()
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
await sem.acquire()
loop.create_task(fetch(session, url))
async def fetch(session, url):
async with session.get(url) as response:
match_json = await response.json()
process(match_json)
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
async with sem:
loop.create_task(fetch(session, url))
async def fetch(session, url):
async with session.get(url) as response:
match_json = await response.json()
process(match_json)
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
async with sem:
loop.create_task(fetch(session, url))
Размер семафора все еще нужно подбирать эксперементально
async def fetch(session, url):
async with session.get(url) as response:
match_json = await response.json()
process(match_json)
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
async with sem:
loop.create_task(fetch(session, url))
Данные изменяются в разных частях кода. Нет гарантий точности
async def fetch(session, url):
async with session.get(url) as response:
match_json = await response.json()
process(match_json)
async def main():
async with aiohttp.ClientSession() as session:
for db_server, db_port in db_addresses:
while True:
async with sem:
loop.create_task(fetch(session, url))
Можно упереться в нагрузку на стороне сервера с базой
Хочется делать N паралельных запросов к каждому серверу
Queue
Читаем данные из любого места в коде
Изменяем данные только в одном месте
Гарантирован порядок выполнения задач
Thinking about Concurrency
Raymond Hettinger, Python core developer
https://youtu.be/Bv25Dwe84g0
●
Максимальный доступ к базам
●
Результат собирается в одном месте
admin_queue = asyncio.Queue(maxsize = 1000)
async def read_from_admin_queue():
while True:
match_json = await admin_queue.get()
process(match_json)
admin_queue.task_done()
async def fetch_from_db(url):
while True:
async with session.get(url) as response():
match_json = await response.json()
await admin_queue.put(match_json)
async def main():
loop.create_task(read_from_admin_queue())
for db_server, db_port in db_addresses:
loop.create_task(fetch_from_db(url))
Все круто пока игроки помещаются в память. А если нет?
Все круто пока игроки помещаются в память. А если нет?
while True:
for player_id, player_matches in players_matches_list:
player = Player(player_id)
for match_id in player_matches:
match_json = r.get('.../getMatch/{}'.format(match_id))
player.parse_match(match_json)
db.update(player)
Все круто пока игроки помещаются в память. А если нет?
while True:
for player_id, player_matches in players_matches_list:
player = Player(player_id)
for match_id in player_matches:
match_json = r.get('.../getMatch/{}'.format(match_id))
player.parse_match(match_json)
db.update(player)
База становится очень зависима от скорости random read.
SSD ~ 40 000RPS
Выгодно брать больше серверов с большим кол-вом дисков
Hash Ring
players
Player
matches list
10-15 000 RPS
Java
Децентрализованая архитектура
1: /getMatch/{id}
2: /getMatch/{id}
3: /getMatch/{id}
4: /getMatch/{id}
5: /getMatch/{id}
40: /getMatch/{id}
...
/getNextPlayerMatchesList/{db_id}
player_db_1
player_db_2
player_db_3
player_db_4
player_db_20
…
read_only для всех баз кроме последней
Можно делать частичную выборку игроков
Можно писать один код для batch и online обновлений
async def get_match_json(db_server, db_port, match_id):
async with session.get(url.format(db_server, db_port, match_id)) as
response:
return await response.json()
for player_id, player_matches in players_matches_list:
player = Player(player_id)
tasks = await asyncio.gather(*[get_match_json(db_server, db_port,
match_id) for db_server, db_port, match_id in player_matches])
for match_json in tasks.results():
player.parse_match(match_json)
db.update(player)
async def get_match_json(db_server, db_port, match_id):
async with session.get(url.format(db_server, db_port, match_id)) as
response:
return await response.json()
for player_id, player_matches in players_matches_list:
player = Player(player_id)
tasks = await asyncio.gather(*[get_match_json(db_server, db_port,
match_id) for db_server, db_port, match_id in player_matches])
for match_json in tasks.results():
player.parse_match(match_json)
db.update(player)
Gather может собирать результаты функции
async def get_match_json(db_server, db_port, match_id):
async with session.get(url.format(db_server, db_port, match_id)) as
response:
return await response.json()
for player_id, player_matches in players_matches_list:
player = Player(player_id)
tasks = await asyncio.gather(*[get_match_json(db_server, db_port,
match_id) for db_server, db_port, match_id in player_matches])
for match_json in tasks.results():
player.parse_match(match_json)
db.update(player)
Одновременно процесим только одного игрока ;-(
async def read_from_admin_queue():
players = {}
total_player_matches = {}
cur_matches_precessed = {}
while True:
task_name, task_data = await admin_queue.get()
if task_name == 'new player':
player_id = task_data['player_id']
total_player_matches = task_data['total_player_matches']
players[player_id] = Player(player_id)
total_player_matches[player_id] = total_player_matches
cur_matches_precessed[player_id] = 0
elif task_name == 'new match':
player_id = task_data['player_id']
match_json = task_data['match_json']
players[player_id].parse_match(match_json)
cur_matches_precessed[player_id] += 1
if cur_matches_precessed[player_id] == total_player_matchesplayer_id:
db.update(player)
admin_queue.task_done()
async def main():
url = 'http://{}:{}/getNextMatch'
loop.create_task(read_from_admin_queue())
for db_name, db_server, db_port in db_addresses:
matches_db_queues[db_name] = asyncio.Queue(maxsize = 100)
loop.create_task(fetch_from_match_db(db_server, db_port,
matches_db_queues[db_name]))
loop.create_task(fetch_from_players_matches_list())
async def fetch_from_match_db(db_server, db_port, queue):
while True:
match_id = await queue.get()
url = 'http://{}:{}/getMatch/{}'.format(db_server, db_port, match_id)
async with session.get(url) as response():
match_json = await response.json()
await admin_queue.put(('new match', {'match_json': match_json}))
queue.task_done()
async def fetch_from_players_matches_list():
for player_id, player_matches in players_matches_list:
await admin_queue.put(('new player', {
'player_id': player_id,
'total_player_matches': len(player_matches)
}))
await asyncio.gather(*[
matches_db_queues[db_name].put(match_id) for
db_name, match_id in player_matches
])
Каждый матч придется читать не 1 раз, а в N раз больше.
N — кол-во игроков в одном матче
HTTP слишком медленный протокол для такого кол-ва операций
~ 1000-1500 запросов в секунду при 100% CPU
HTTP слишком медленный протокол для такого кол-ва операций
~ 1000-1500 запросов в секунду при 100% CPU
HTTP лучше заменить на web socket
~ 10 000 запросов в секунду без нагрузки на CPU
(можно использовать RabbitMQ или Apache Kafka)
async def fetch_from_socket(db_name):
async with ClientSession() as session:
async with session.ws_connect('http://
{}/socket'.format(db_name)) as ws:
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
match_json = json.load(msg.data)
await admin_queue.put(match_json)
async def fetch_from_socket(db_name):
async with ClientSession() as session:
async with session.ws_connect('http://
{}/socket'.format(db_name)) as ws:
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
match_json = json.load(msg.data)
await admin_queue.put(match_json)
Пока ждем ответа от сервера негде послать сообщение
async def fetch_from_socket(db_name):
async with ClientSession() as session:
async with session.ws_connect('http://
{}/socket'.format(db_name)) as ws:
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
match_json = json.load(msg.data)
await admin_queue.put(match_json)
async def send_to_socket(ws):
while True:
match_id = await matches_db_queues.get()
await ws.send_str('get next match {}'.format(match_id))
matches_db_queues.task_done()
Даже если мы может процесить несколько игроков одновременно,
их игры будет неравномерно распределены по базам с матчами
Даже если мы может процесить несколько игроков одновременно,
их игры будет неравномерно распределены по базам с матчами
Даже если мы может процесить несколько игроков одновременно,
их игры будет неравномерно распределены по базам с матчами
Даже если мы может процесить несколько игроков одновременно,
их игры будет неравномерно распределены по базам с матчами
Одна база все равно используется неэффективно
Даже если мы может процесить несколько игроков одновременно,
их игры будет неравномерно распределены по базам с матчами
Выгоднее один раз посчитать такие наборы игроков,
что их обращение к базам будет наиболее эффективно
Итог
●
CPU на 100% на базах с матчами и игроками
●
Скорость ограничена SSD random read
●
Гибко
●
Понятно
●
Легко мониторить

More Related Content

What's hot

Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьYandex
 
05 - Web-технологии. Сетевые протоколы
05 - Web-технологии. Сетевые протоколы05 - Web-технологии. Сетевые протоколы
05 - Web-технологии. Сетевые протоколы
Roman Brovko
 
Web осень 2013 лекция 5
Web осень 2013 лекция 5Web осень 2013 лекция 5
Web осень 2013 лекция 5Technopark
 
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)Ontico
 
SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода
SECON'2014 - Павел Щеваев - Метаданные и автогенерация кодаSECON'2014 - Павел Щеваев - Метаданные и автогенерация кода
SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода
Конференция разработчиков программного обеспечения SECON'2014
 
Python
PythonPython
Pythonpelid
 
Отладка в Erlang, trace/dbg
Отладка в Erlang, trace/dbgОтладка в Erlang, trace/dbg
Отладка в Erlang, trace/dbg
Yuri Zhloba
 
Разработка на блокчейн Голос | Ерлан Шиндаулетов
Разработка на блокчейн Голос | Ерлан ШиндаулетовРазработка на блокчейн Голос | Ерлан Шиндаулетов
Разработка на блокчейн Голос | Ерлан Шиндаулетов
Cyber Fund
 
Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...
Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...
Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...
Yandex
 
Python infrastructure from scratch
Python infrastructure from scratchPython infrastructure from scratch
Python infrastructure from scratch
Виктор Тыщенко
 
Yandex Tank - Арсений Фомченко
Yandex Tank - Арсений ФомченкоYandex Tank - Арсений Фомченко
Yandex Tank - Арсений Фомченко
AvitoTech
 
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
Badoo Development
 
JavaScript на сервере, 1ms на трансформацию (Андрей Сумин)
JavaScript на сервере, 1ms на трансформацию (Андрей Сумин)JavaScript на сервере, 1ms на трансформацию (Андрей Сумин)
JavaScript на сервере, 1ms на трансформацию (Андрей Сумин)Ontico
 
Пожиратели памяти в WordPress
Пожиратели памяти в WordPressПожиратели памяти в WordPress
Пожиратели памяти в WordPress
Konstantin Kovshenin
 
Android: низкоуровневые мультимедиа API и их применение в реальной жизни
Android: низкоуровневые мультимедиа API и их применение в реальной жизниAndroid: низкоуровневые мультимедиа API и их применение в реальной жизни
Android: низкоуровневые мультимедиа API и их применение в реальной жизни
Gregory Klyushnikov
 
Андрей Дроздов "Создание высокопроизводительных rest api на tarantool"
Андрей Дроздов "Создание высокопроизводительных rest api на tarantool"Андрей Дроздов "Создание высокопроизводительных rest api на tarantool"
Андрей Дроздов "Создание высокопроизводительных rest api на tarantool"
Tanya Denisyuk
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Python Meetup
 
Hacking PostgreSQL. Локальная память процессов. Контексты памяти.
Hacking PostgreSQL. Локальная память процессов. Контексты памяти.Hacking PostgreSQL. Локальная память процессов. Контексты памяти.
Hacking PostgreSQL. Локальная память процессов. Контексты памяти.
Anastasia Lubennikova
 

What's hot (20)

Михаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: АсинхронностьМихаил Давыдов — JavaScript: Асинхронность
Михаил Давыдов — JavaScript: Асинхронность
 
05 - Web-технологии. Сетевые протоколы
05 - Web-технологии. Сетевые протоколы05 - Web-технологии. Сетевые протоколы
05 - Web-технологии. Сетевые протоколы
 
Web осень 2013 лекция 5
Web осень 2013 лекция 5Web осень 2013 лекция 5
Web осень 2013 лекция 5
 
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
Приёмы разработки высоконагруженных приложений на Twisted (Андрей Смирнов)
 
Асинхронный JavaScript
Асинхронный JavaScriptАсинхронный JavaScript
Асинхронный JavaScript
 
SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода
SECON'2014 - Павел Щеваев - Метаданные и автогенерация кодаSECON'2014 - Павел Щеваев - Метаданные и автогенерация кода
SECON'2014 - Павел Щеваев - Метаданные и автогенерация кода
 
Python
PythonPython
Python
 
Отладка в Erlang, trace/dbg
Отладка в Erlang, trace/dbgОтладка в Erlang, trace/dbg
Отладка в Erlang, trace/dbg
 
Разработка на блокчейн Голос | Ерлан Шиндаулетов
Разработка на блокчейн Голос | Ерлан ШиндаулетовРазработка на блокчейн Голос | Ерлан Шиндаулетов
Разработка на блокчейн Голос | Ерлан Шиндаулетов
 
Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...
Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...
Дмитрий Жестилевский — Yet another threading framework: асинхронная разработк...
 
Python infrastructure from scratch
Python infrastructure from scratchPython infrastructure from scratch
Python infrastructure from scratch
 
Yandex Tank - Арсений Фомченко
Yandex Tank - Арсений ФомченкоYandex Tank - Арсений Фомченко
Yandex Tank - Арсений Фомченко
 
Erlang tasty & useful stuff
Erlang tasty & useful stuffErlang tasty & useful stuff
Erlang tasty & useful stuff
 
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
 
JavaScript на сервере, 1ms на трансформацию (Андрей Сумин)
JavaScript на сервере, 1ms на трансформацию (Андрей Сумин)JavaScript на сервере, 1ms на трансформацию (Андрей Сумин)
JavaScript на сервере, 1ms на трансформацию (Андрей Сумин)
 
Пожиратели памяти в WordPress
Пожиратели памяти в WordPressПожиратели памяти в WordPress
Пожиратели памяти в WordPress
 
Android: низкоуровневые мультимедиа API и их применение в реальной жизни
Android: низкоуровневые мультимедиа API и их применение в реальной жизниAndroid: низкоуровневые мультимедиа API и их применение в реальной жизни
Android: низкоуровневые мультимедиа API и их применение в реальной жизни
 
Андрей Дроздов "Создание высокопроизводительных rest api на tarantool"
Андрей Дроздов "Создание высокопроизводительных rest api на tarantool"Андрей Дроздов "Создание высокопроизводительных rest api на tarantool"
Андрей Дроздов "Создание высокопроизводительных rest api на tarantool"
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Hacking PostgreSQL. Локальная память процессов. Контексты памяти.
Hacking PostgreSQL. Локальная память процессов. Контексты памяти.Hacking PostgreSQL. Локальная память процессов. Контексты памяти.
Hacking PostgreSQL. Локальная память процессов. Контексты памяти.
 

Similar to Asyncio для процессинга распределенной базы данных

Суперсилы Chrome developer tools
Суперсилы Chrome developer toolsСуперсилы Chrome developer tools
Суперсилы Chrome developer tools
2ГИС Технологии
 
Почему Mojolicious?
Почему Mojolicious?Почему Mojolicious?
Почему Mojolicious?
Anatoly Sharifulin
 
Компиляция скриптов PHP. Алексей Романенко
Компиляция скриптов PHP. Алексей РоманенкоКомпиляция скриптов PHP. Алексей Романенко
Компиляция скриптов PHP. Алексей РоманенкоFuenteovejuna
 
Компиляция скриптов PHP (Алексей Романенко)
Компиляция скриптов PHP (Алексей Романенко)Компиляция скриптов PHP (Алексей Романенко)
Компиляция скриптов PHP (Алексей Романенко)Ontico
 
Эффективное программирование на NodeJS
Эффективное программирование на NodeJSЭффективное программирование на NodeJS
Эффективное программирование на NodeJS
Yura Bogdanov
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6Technopark
 
Доставка данных в реальном времени.
Доставка данных в реальном времени. Доставка данных в реальном времени.
Доставка данных в реальном времени.
beshkenadze
 
17 - Web-технологии. Real Time сообщения
17 - Web-технологии. Real Time сообщения17 - Web-технологии. Real Time сообщения
17 - Web-технологии. Real Time сообщения
Roman Brovko
 
Нельзя просто так взять и сделать версионирование API
Нельзя просто так взять и сделать версионирование APIНельзя просто так взять и сделать версионирование API
Нельзя просто так взять и сделать версионирование API
EatDog
 
JavaScript. Async (in Russian)
JavaScript. Async (in Russian)JavaScript. Async (in Russian)
JavaScript. Async (in Russian)Mikhail Davydov
 
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
DevGAMM Conference
 
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...ZFConf Conference
 
Node.JS: возможности для РНР-разработчика
Node.JS: возможности для РНР-разработчикаNode.JS: возможности для РНР-разработчика
Node.JS: возможности для РНР-разработчика
Alexei Smolyanov
 
Hacking PostgreSQL. Обзор исходного кода
Hacking PostgreSQL. Обзор исходного кодаHacking PostgreSQL. Обзор исходного кода
Hacking PostgreSQL. Обзор исходного кода
Anastasia Lubennikova
 
Мульти-доменность в Django проекте
Мульти-доменность в Django проектеМульти-доменность в Django проекте
Мульти-доменность в Django проекте
Alexey Kinyov
 
PHP Tricks
PHP TricksPHP Tricks
PHP TricksBlackFan
 
Стажировка-2014, занятие 8. Обзор Sails framework (Node.js)
Стажировка-2014, занятие 8. Обзор Sails framework (Node.js)Стажировка-2014, занятие 8. Обзор Sails framework (Node.js)
Стажировка-2014, занятие 8. Обзор Sails framework (Node.js)7bits
 

Similar to Asyncio для процессинга распределенной базы данных (20)

Суперсилы Chrome developer tools
Суперсилы Chrome developer toolsСуперсилы Chrome developer tools
Суперсилы Chrome developer tools
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
Почему Mojolicious?
Почему Mojolicious?Почему Mojolicious?
Почему Mojolicious?
 
Компиляция скриптов PHP. Алексей Романенко
Компиляция скриптов PHP. Алексей РоманенкоКомпиляция скриптов PHP. Алексей Романенко
Компиляция скриптов PHP. Алексей Романенко
 
Компиляция скриптов PHP (Алексей Романенко)
Компиляция скриптов PHP (Алексей Романенко)Компиляция скриптов PHP (Алексей Романенко)
Компиляция скриптов PHP (Алексей Романенко)
 
Эффективное программирование на NodeJS
Эффективное программирование на NodeJSЭффективное программирование на NodeJS
Эффективное программирование на NodeJS
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
 
Доставка данных в реальном времени.
Доставка данных в реальном времени. Доставка данных в реальном времени.
Доставка данных в реальном времени.
 
17 - Web-технологии. Real Time сообщения
17 - Web-технологии. Real Time сообщения17 - Web-технологии. Real Time сообщения
17 - Web-технологии. Real Time сообщения
 
Нельзя просто так взять и сделать версионирование API
Нельзя просто так взять и сделать версионирование APIНельзя просто так взять и сделать версионирование API
Нельзя просто так взять и сделать версионирование API
 
JavaScript. Async (in Russian)
JavaScript. Async (in Russian)JavaScript. Async (in Russian)
JavaScript. Async (in Russian)
 
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
Транзакционный фреймворк для сингловых игр и игр с асинхронным мультиплеером ...
 
php frameworks
php frameworksphp frameworks
php frameworks
 
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...
ZFConf 2011: Разделение труда: Организация многозадачной, распределенной сист...
 
Node.JS: возможности для РНР-разработчика
Node.JS: возможности для РНР-разработчикаNode.JS: возможности для РНР-разработчика
Node.JS: возможности для РНР-разработчика
 
Hacking PostgreSQL. Обзор исходного кода
Hacking PostgreSQL. Обзор исходного кодаHacking PostgreSQL. Обзор исходного кода
Hacking PostgreSQL. Обзор исходного кода
 
Мульти-доменность в Django проекте
Мульти-доменность в Django проектеМульти-доменность в Django проекте
Мульти-доменность в Django проекте
 
PHP Tricks
PHP TricksPHP Tricks
PHP Tricks
 
Стажировка-2014, занятие 8. Обзор Sails framework (Node.js)
Стажировка-2014, занятие 8. Обзор Sails framework (Node.js)Стажировка-2014, занятие 8. Обзор Sails framework (Node.js)
Стажировка-2014, занятие 8. Обзор Sails framework (Node.js)
 
бегун
бегунбегун
бегун
 

More from PyNSK

Нейронные сети и Keras. Часть 1
Нейронные сети и Keras. Часть 1Нейронные сети и Keras. Часть 1
Нейронные сети и Keras. Часть 1
PyNSK
 
Нейронные сети и Keras. Часть 2
Нейронные сети и Keras. Часть 2Нейронные сети и Keras. Часть 2
Нейронные сети и Keras. Часть 2
PyNSK
 
Python для GameDev
Python для GameDevPython для GameDev
Python для GameDev
PyNSK
 
Python инструменты для нагрузочного тестирования
Python инструменты для нагрузочного тестированияPython инструменты для нагрузочного тестирования
Python инструменты для нагрузочного тестирования
PyNSK
 
Python, Django и корпоративные информационные системы
Python, Django и корпоративные информационные системыPython, Django и корпоративные информационные системы
Python, Django и корпоративные информационные системы
PyNSK
 
Настрой контент под пользователя!
Настрой контент под пользователя!Настрой контент под пользователя!
Настрой контент под пользователя!
PyNSK
 
Питон в малине
Питон в малинеПитон в малине
Питон в малине
PyNSK
 
Мой Python всегда со мной!
Мой Python всегда со мной!Мой Python всегда со мной!
Мой Python всегда со мной!
PyNSK
 
Как и зачем можно создать DSL на Python
Как и зачем можно создать DSL на PythonКак и зачем можно создать DSL на Python
Как и зачем можно создать DSL на Python
PyNSK
 
Чем Python плох для стартапа?
Чем Python плох для стартапа?Чем Python плох для стартапа?
Чем Python плох для стартапа?
PyNSK
 
Во внутренности Kivy
Во внутренности KivyВо внутренности Kivy
Во внутренности Kivy
PyNSK
 
Зоопарк python веб-фреймворков
Зоопарк python веб-фреймворковЗоопарк python веб-фреймворков
Зоопарк python веб-фреймворков
PyNSK
 
Как Python Дайджест работает с внешней статикой
Как Python Дайджест работает с внешней статикойКак Python Дайджест работает с внешней статикой
Как Python Дайджест работает с внешней статикой
PyNSK
 
Применение behave+webdriver для тестирования Web-проектов
Применение behave+webdriver для тестирования Web-проектовПрименение behave+webdriver для тестирования Web-проектов
Применение behave+webdriver для тестирования Web-проектов
PyNSK
 
Ctypes в игровых приложениях на python
Ctypes в игровых приложениях на pythonCtypes в игровых приложениях на python
Ctypes в игровых приложениях на python
PyNSK
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности Python
PyNSK
 
Python инструменты решения типичных задач
Python  инструменты решения типичных задачPython  инструменты решения типичных задач
Python инструменты решения типичных задач
PyNSK
 
Мир Python функционалим с помощью библиотек
Мир Python  функционалим с помощью библиотекМир Python  функционалим с помощью библиотек
Мир Python функционалим с помощью библиотек
PyNSK
 
JSON-RPC или когда rest неудобен
JSON-RPC или когда rest неудобенJSON-RPC или когда rest неудобен
JSON-RPC или когда rest неудобен
PyNSK
 
TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.
PyNSK
 

More from PyNSK (20)

Нейронные сети и Keras. Часть 1
Нейронные сети и Keras. Часть 1Нейронные сети и Keras. Часть 1
Нейронные сети и Keras. Часть 1
 
Нейронные сети и Keras. Часть 2
Нейронные сети и Keras. Часть 2Нейронные сети и Keras. Часть 2
Нейронные сети и Keras. Часть 2
 
Python для GameDev
Python для GameDevPython для GameDev
Python для GameDev
 
Python инструменты для нагрузочного тестирования
Python инструменты для нагрузочного тестированияPython инструменты для нагрузочного тестирования
Python инструменты для нагрузочного тестирования
 
Python, Django и корпоративные информационные системы
Python, Django и корпоративные информационные системыPython, Django и корпоративные информационные системы
Python, Django и корпоративные информационные системы
 
Настрой контент под пользователя!
Настрой контент под пользователя!Настрой контент под пользователя!
Настрой контент под пользователя!
 
Питон в малине
Питон в малинеПитон в малине
Питон в малине
 
Мой Python всегда со мной!
Мой Python всегда со мной!Мой Python всегда со мной!
Мой Python всегда со мной!
 
Как и зачем можно создать DSL на Python
Как и зачем можно создать DSL на PythonКак и зачем можно создать DSL на Python
Как и зачем можно создать DSL на Python
 
Чем Python плох для стартапа?
Чем Python плох для стартапа?Чем Python плох для стартапа?
Чем Python плох для стартапа?
 
Во внутренности Kivy
Во внутренности KivyВо внутренности Kivy
Во внутренности Kivy
 
Зоопарк python веб-фреймворков
Зоопарк python веб-фреймворковЗоопарк python веб-фреймворков
Зоопарк python веб-фреймворков
 
Как Python Дайджест работает с внешней статикой
Как Python Дайджест работает с внешней статикойКак Python Дайджест работает с внешней статикой
Как Python Дайджест работает с внешней статикой
 
Применение behave+webdriver для тестирования Web-проектов
Применение behave+webdriver для тестирования Web-проектовПрименение behave+webdriver для тестирования Web-проектов
Применение behave+webdriver для тестирования Web-проектов
 
Ctypes в игровых приложениях на python
Ctypes в игровых приложениях на pythonCtypes в игровых приложениях на python
Ctypes в игровых приложениях на python
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности Python
 
Python инструменты решения типичных задач
Python  инструменты решения типичных задачPython  инструменты решения типичных задач
Python инструменты решения типичных задач
 
Мир Python функционалим с помощью библиотек
Мир Python  функционалим с помощью библиотекМир Python  функционалим с помощью библиотек
Мир Python функционалим с помощью библиотек
 
JSON-RPC или когда rest неудобен
JSON-RPC или когда rest неудобенJSON-RPC или когда rest неудобен
JSON-RPC или когда rest неудобен
 
TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.
 

Asyncio для процессинга распределенной базы данных

  • 2. О себе ● Никита Семенов ● 5 лет играл в покер ● R, scikit-learn, data mining ● 1 год python backend в pickorban.com
  • 4. О чем доклад ● asyncio ● aiohttp, web socket ● gather, semaphore, asyncio.Queue ● От плохого к хорошему
  • 6.
  • 7. Matches dbs Users dbsBlack Box 1 000 000 000 игр 1 тб сжатых данных 70 тб скачено через Dota Web API 10 000 000 игроков 500 гб данных Key:value +1 000 000 каждый день GET /getMatch/{match_id} GET /getNextMatch
  • 8. Решение #1 sequential read: Читаем все матчи подряд и апдейтим игроков SSD 200 MBsec, HDD 100 MBsec
  • 9. Решение #1 sequential read: Читаем все матчи подряд и апдейтим игроков import requests from config import db_addresses for db_server, db_port in db_addresses: while not error: r = requests.get('http://{}:{}/getNextMatch'.format(db_server, db_port)) match_json = r.json() process(match_json)
  • 11. Как хочется? ● Чтобы использовалось 100% CPU Решение Task 1: In process Task 2: In process Task 3: Finish Task 4: Canceled
  • 12. from concurrent.futures import ThreadPoolExecutor def fetch(server, port): r = requests.get('http://{}:{}/getNextMatch'.format(server, port)) process(r.json()) with ThreadPoolExecutor(max_workers = 10) as executor: for db_server, db_port in db_addresses: while True: for _ in range(10): executor.submit(fetch, db_server, db_port) sleep(0.015)
  • 13. from concurrent.futures import ThreadPoolExecutor def fetch(server, port): r = requests.get('http://{}:{}/getNextMatch'.format(server, port)) process(r.json()) with ThreadPoolExecutor(max_workers = 10) as executor: for db_server, db_port in db_addresses: while True: for _ in range(10): executor.submit(fetch, db_server, db_port) sleep(0.015) Накрасивый вызов аргументов
  • 14. from concurrent.futures import ThreadPoolExecutor def fetch(server, port): r = requests.get('http://{}:{}/getNextMatch'.format(server, port)) process(r.json()) with ThreadPoolExecutor(max_workers = 10) as executor: for db_server, db_port in db_addresses: while True: for _ in range(10): executor.submit(fetch, db_server, db_port) sleep(0.015) Параметры придется подбирать эксперементально
  • 15. from concurrent.futures import ThreadPoolExecutor def fetch(server, port): r = requests.get('http://{}:{}/getNextMatch'.format(server, port)) process(r.json()) with ThreadPoolExecutor(max_workers = 10) as executor: for db_server, db_port in db_addresses: while True: for _ in range(10): executor.submit(fetch, db_server, db_port) sleep(0.015) Треды не бесконечны и затрагивают OS
  • 16. from concurrent.futures import ThreadPoolExecutor def fetch(server, port): r = requests.get('http://{}:{}/getNextMatch'.format(server, port)) process(r.json()) with ThreadPoolExecutor(max_workers = 10) as executor: for db_server, db_port in db_addresses: while True: for _ in range(10): executor.submit(fetch, db_server, db_port) sleep(0.015) Неявное отличие блокирующих функций от неблокирующих
  • 17. import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: match_json = await response.json() process(match_json) async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: for _ in range(10): loop.create_task(fetch(session, url)) await asyncio.sleep(0.015) loop = asyncio.get_event_loop() loop.run_until_complete(main())
  • 18. import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: match_json = await response.json() process(match_json) async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: for _ in range(10): loop.create_task(fetch(session, url)) await asyncio.sleep(0.015) loop = asyncio.get_event_loop() loop.run_until_complete(main()) 1 скрипт = 1 loop, 1 thread
  • 19. import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: match_json = await response.json() process(match_json) async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: for _ in range(10): loop.create_task(fetch(session, url)) await asyncio.sleep(0.015) loop = asyncio.get_event_loop() loop.run_until_complete(main()) Все еще проблема со sleep и синтаксисом
  • 20. async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: tasks = [] for _ in range(10): tasks.append(fetch(session, url)) await asyncio.gather(*tasks)
  • 21. async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: tasks = [] for _ in range(10): tasks.append(fetch(session, url)) await asyncio.gather(*tasks) Gather работает со скоростью самого медленного запроса
  • 22. sem = asyncio.Semaphore(10) async def fetch(session, url): async with session.get(url) as response: match_json = await response.json() process(match_json) sem.release() async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: await sem.acquire() loop.create_task(fetch(session, url))
  • 23. async def fetch(session, url): async with session.get(url) as response: match_json = await response.json() process(match_json) async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: async with sem: loop.create_task(fetch(session, url))
  • 24. async def fetch(session, url): async with session.get(url) as response: match_json = await response.json() process(match_json) async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: async with sem: loop.create_task(fetch(session, url)) Размер семафора все еще нужно подбирать эксперементально
  • 25. async def fetch(session, url): async with session.get(url) as response: match_json = await response.json() process(match_json) async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: async with sem: loop.create_task(fetch(session, url)) Данные изменяются в разных частях кода. Нет гарантий точности
  • 26. async def fetch(session, url): async with session.get(url) as response: match_json = await response.json() process(match_json) async def main(): async with aiohttp.ClientSession() as session: for db_server, db_port in db_addresses: while True: async with sem: loop.create_task(fetch(session, url)) Можно упереться в нагрузку на стороне сервера с базой Хочется делать N паралельных запросов к каждому серверу
  • 27. Queue Читаем данные из любого места в коде Изменяем данные только в одном месте Гарантирован порядок выполнения задач Thinking about Concurrency Raymond Hettinger, Python core developer https://youtu.be/Bv25Dwe84g0
  • 28. ● Максимальный доступ к базам ● Результат собирается в одном месте admin_queue = asyncio.Queue(maxsize = 1000) async def read_from_admin_queue(): while True: match_json = await admin_queue.get() process(match_json) admin_queue.task_done() async def fetch_from_db(url): while True: async with session.get(url) as response(): match_json = await response.json() await admin_queue.put(match_json) async def main(): loop.create_task(read_from_admin_queue()) for db_server, db_port in db_addresses: loop.create_task(fetch_from_db(url))
  • 29. Все круто пока игроки помещаются в память. А если нет?
  • 30. Все круто пока игроки помещаются в память. А если нет? while True: for player_id, player_matches in players_matches_list: player = Player(player_id) for match_id in player_matches: match_json = r.get('.../getMatch/{}'.format(match_id)) player.parse_match(match_json) db.update(player)
  • 31. Все круто пока игроки помещаются в память. А если нет? while True: for player_id, player_matches in players_matches_list: player = Player(player_id) for match_id in player_matches: match_json = r.get('.../getMatch/{}'.format(match_id)) player.parse_match(match_json) db.update(player) База становится очень зависима от скорости random read. SSD ~ 40 000RPS Выгодно брать больше серверов с большим кол-вом дисков
  • 34. Децентрализованая архитектура 1: /getMatch/{id} 2: /getMatch/{id} 3: /getMatch/{id} 4: /getMatch/{id} 5: /getMatch/{id} 40: /getMatch/{id} ... /getNextPlayerMatchesList/{db_id} player_db_1 player_db_2 player_db_3 player_db_4 player_db_20 …
  • 35. read_only для всех баз кроме последней Можно делать частичную выборку игроков Можно писать один код для batch и online обновлений
  • 36. async def get_match_json(db_server, db_port, match_id): async with session.get(url.format(db_server, db_port, match_id)) as response: return await response.json() for player_id, player_matches in players_matches_list: player = Player(player_id) tasks = await asyncio.gather(*[get_match_json(db_server, db_port, match_id) for db_server, db_port, match_id in player_matches]) for match_json in tasks.results(): player.parse_match(match_json) db.update(player)
  • 37. async def get_match_json(db_server, db_port, match_id): async with session.get(url.format(db_server, db_port, match_id)) as response: return await response.json() for player_id, player_matches in players_matches_list: player = Player(player_id) tasks = await asyncio.gather(*[get_match_json(db_server, db_port, match_id) for db_server, db_port, match_id in player_matches]) for match_json in tasks.results(): player.parse_match(match_json) db.update(player) Gather может собирать результаты функции
  • 38. async def get_match_json(db_server, db_port, match_id): async with session.get(url.format(db_server, db_port, match_id)) as response: return await response.json() for player_id, player_matches in players_matches_list: player = Player(player_id) tasks = await asyncio.gather(*[get_match_json(db_server, db_port, match_id) for db_server, db_port, match_id in player_matches]) for match_json in tasks.results(): player.parse_match(match_json) db.update(player) Одновременно процесим только одного игрока ;-(
  • 39. async def read_from_admin_queue(): players = {} total_player_matches = {} cur_matches_precessed = {} while True: task_name, task_data = await admin_queue.get() if task_name == 'new player': player_id = task_data['player_id'] total_player_matches = task_data['total_player_matches'] players[player_id] = Player(player_id) total_player_matches[player_id] = total_player_matches cur_matches_precessed[player_id] = 0 elif task_name == 'new match': player_id = task_data['player_id'] match_json = task_data['match_json'] players[player_id].parse_match(match_json) cur_matches_precessed[player_id] += 1 if cur_matches_precessed[player_id] == total_player_matchesplayer_id: db.update(player) admin_queue.task_done()
  • 40. async def main(): url = 'http://{}:{}/getNextMatch' loop.create_task(read_from_admin_queue()) for db_name, db_server, db_port in db_addresses: matches_db_queues[db_name] = asyncio.Queue(maxsize = 100) loop.create_task(fetch_from_match_db(db_server, db_port, matches_db_queues[db_name])) loop.create_task(fetch_from_players_matches_list())
  • 41. async def fetch_from_match_db(db_server, db_port, queue): while True: match_id = await queue.get() url = 'http://{}:{}/getMatch/{}'.format(db_server, db_port, match_id) async with session.get(url) as response(): match_json = await response.json() await admin_queue.put(('new match', {'match_json': match_json})) queue.task_done()
  • 42. async def fetch_from_players_matches_list(): for player_id, player_matches in players_matches_list: await admin_queue.put(('new player', { 'player_id': player_id, 'total_player_matches': len(player_matches) })) await asyncio.gather(*[ matches_db_queues[db_name].put(match_id) for db_name, match_id in player_matches ])
  • 43. Каждый матч придется читать не 1 раз, а в N раз больше. N — кол-во игроков в одном матче
  • 44. HTTP слишком медленный протокол для такого кол-ва операций ~ 1000-1500 запросов в секунду при 100% CPU
  • 45. HTTP слишком медленный протокол для такого кол-ва операций ~ 1000-1500 запросов в секунду при 100% CPU HTTP лучше заменить на web socket ~ 10 000 запросов в секунду без нагрузки на CPU (можно использовать RabbitMQ или Apache Kafka)
  • 46. async def fetch_from_socket(db_name): async with ClientSession() as session: async with session.ws_connect('http:// {}/socket'.format(db_name)) as ws: async for msg in ws: if msg.type == aiohttp.WSMsgType.TEXT: match_json = json.load(msg.data) await admin_queue.put(match_json)
  • 47. async def fetch_from_socket(db_name): async with ClientSession() as session: async with session.ws_connect('http:// {}/socket'.format(db_name)) as ws: async for msg in ws: if msg.type == aiohttp.WSMsgType.TEXT: match_json = json.load(msg.data) await admin_queue.put(match_json) Пока ждем ответа от сервера негде послать сообщение
  • 48. async def fetch_from_socket(db_name): async with ClientSession() as session: async with session.ws_connect('http:// {}/socket'.format(db_name)) as ws: async for msg in ws: if msg.type == aiohttp.WSMsgType.TEXT: match_json = json.load(msg.data) await admin_queue.put(match_json) async def send_to_socket(ws): while True: match_id = await matches_db_queues.get() await ws.send_str('get next match {}'.format(match_id)) matches_db_queues.task_done()
  • 49. Даже если мы может процесить несколько игроков одновременно, их игры будет неравномерно распределены по базам с матчами
  • 50. Даже если мы может процесить несколько игроков одновременно, их игры будет неравномерно распределены по базам с матчами
  • 51. Даже если мы может процесить несколько игроков одновременно, их игры будет неравномерно распределены по базам с матчами
  • 52. Даже если мы может процесить несколько игроков одновременно, их игры будет неравномерно распределены по базам с матчами Одна база все равно используется неэффективно
  • 53. Даже если мы может процесить несколько игроков одновременно, их игры будет неравномерно распределены по базам с матчами Выгоднее один раз посчитать такие наборы игроков, что их обращение к базам будет наиболее эффективно
  • 54. Итог ● CPU на 100% на базах с матчами и игроками ● Скорость ограничена SSD random read ● Гибко ● Понятно ● Легко мониторить