Deep Engineering

ЗАМЕР

bench/load-balancing/ecs.py

Скрипт, которым получены числа в статье, и запись прогона. Файл читается на сборке из репозитория — это тот самый код, который запускали, а не его копия.

Цитируется в статье
/ru/system-design/traffic/load-balancing
Как запустить
python3 bins.py
python3 sim2.py
python3 sim3.py
python3 herd.py
python3 herd2.py
pip install dnspython && python3 dns2.py && python3 ecs.py

Запись прогона

Замеры для статьи «Балансировка нагрузки»

Два разных вида доказательств, и путать их нельзя.

Живые замеры DNS

dns2.py — ротация ответов: двадцать запросов подряд к трём доменам, печатает число адресов в ответе, диапазон TTL и распределение того, какой адрес оказался первым.

ecs.py — geoDNS через EDNS Client Subnet (RFC 7871): один и тот же вопрос задаётся с подстановкой четырёх клиентских подсетей, печатает адреса и SCOPE PREFIX-LENGTH из ответа.

Требует dnspython и сетевого доступа. Адреса у вас будут другие — они зависят от того, откуда вы спрашиваете и через какой резолвер. Воспроизводится не адрес, а scope: ноль означает «ответ годится всем», ненулевой — «ответ зависит от подсети». Именно на этом различии построен раздел про geoDNS.

Модель очередей

bins.py — чистая задача о шарах и корзинах: n шаров в n корзин, случайный выбор против выбора менее полной из двух. Пятнадцать прогонов на каждое n, рядом печатаются асимптотические оценки ln n / ln ln n и ln ln n / ln 2.

sim2.py — основная модель: шестнадцать бэкендов, каждый — одна очередь FIFO с одним обслуживающим прибором (G/G/1). Пуассоновский поток, логнормальное время обработки. Сравниваются random, round-robin, least-conn, least-time, p2c на одинаковых серверах, на кластере с медленным сервером и при загрузке 95%.

sim3.py — взвешенный round-robin с угаданным, отсутствующим и заниженным весом, и отдельно цена закреплённых сессий при 5000, 500 и 50 живых сессиях.

herd.py — несколько независимых балансировщиков, у каждого свои счётчики.

herd2.py — те же балансировщики, но с общим снимком состояния, устаревающим на refresh. Эти два скрипта дают главный результат статьи и отвечают на разные вопросы: первый показывает, что неполнота данных не ломает least connections, второй — что общая устаревшая ошибка ломает.

Модель отвечает на один вопрос — как распределение запросов влияет на ожидание в очередях. В ней нет сети и её задержек, разрывов соединений, проверок живости, кеша на бэкенде и ограничений по памяти. Числа воспроизводятся точно: зёрна фиксированы, первые 20 000 запросов из 300 000 отбрасываются, результат усредняется по пяти прогонам.

python3 bins.py
python3 sim2.py
python3 sim3.py
python3 herd.py
python3 herd2.py
pip install dnspython && python3 dns2.py && python3 ecs.py

Скрипт

25 строк
import dns.message, dns.query, dns.edns, dns.rdatatype

SUBNETS = [("США, Нью-Йорк", "68.192.0.0", 24), ("Германия, Франкфурт", "91.65.0.0", 24),
           ("Япония, Токио", "133.11.0.0", 24), ("Бразилия, Сан-Паулу", "177.54.0.0", 24)]

def ask(name, ip, prefix, server="8.8.8.8"):
    q = dns.message.make_query(name, "A", use_edns=0, payload=1232,
                               options=[dns.edns.ECSOption(ip, prefix)])
    resp = dns.query.udp(q, server, timeout=6)
    ips = sorted(r.address for rr in resp.answer if rr.rdtype == dns.rdatatype.A for r in rr)
    scope = None
    for o in (resp.options or []):
        if isinstance(o, dns.edns.ECSOption):
            scope = o.scopelen
    return ips, scope

for name in ["www.bing.com", "www.microsoft.com", "d1.awsstatic.com", "www.netflix.com"]:
    print(f"--- {name}")
    for label, ip, plen in SUBNETS:
        try:
            ips, scope = ask(name, ip, plen)
            print(f"   {label:22s} scope=/{scope}  {ips[:3]}")
        except Exception as e:
            print(f"   {label:22s} {type(e).__name__}: {e}")