[{"data":1,"prerenderedAt":95},["ShallowReactive",2],{"api:\u002Fapi\u002Fconfig:ru":3,"api:\u002Fapi\u002Fnotes:ru":8,"api:\u002Fapi\u002Fprojects\u002Fapi-gateway:ru":32},{"email":4,"lang":5,"name":6,"role":7},"hello@ewsirdev.example","ru","Ewsirdev","Веб-разработчик: сайты, лендинги и сервисы на Go",[9,16,22,27],{"slug":10,"lang":5,"title":11,"date":12,"summary":13,"project":14,"minutes":15},"double-booking","Двойная бронь: транзакция плюс частичный UNIQUE-индекс","2026-10-20","Как booking-slots защищается от двух клиентов, которые одновременно записываются на одно время, и чем проверяется эта защита.","booking-slots",1,{"slug":17,"lang":5,"title":18,"date":12,"summary":19,"project":20,"minutes":21},"lease-and-fencing","Lease и fencing: как очередь не теряет задачу и не даёт ей выполниться дважды «зомби»","Воркер может упасть, зависнуть или проснуться, когда его задачу уже забрали. Разбираю, как lease и токен аренды в job-queue решают это в SQLite.","job-queue",2,{"slug":23,"lang":5,"title":24,"date":12,"summary":25,"project":26,"minutes":15},"p95-nearest-rank","p95 в мониторе доступности: метод ближайшего ранга и чего он не скрывает","Как uptime-monitor считает p50\u002Fp95 задержки и аптайм, почему выбран метод ближайшего ранга и где эти цифры обманчивы.","uptime-monitor",{"slug":28,"lang":5,"title":29,"date":12,"summary":30,"project":31,"minutes":21},"websocket-from-scratch","WebSocket на стандартной библиотеке: что на самом деле происходит под new WebSocket()","Зачем в realtime-rooms написан собственный слой RFC 6455, какие нарушения протокола он проверяет и почему для продакшена всё равно лучше взять библиотеку.","realtime-rooms",{"slug":33,"title":34,"type":35,"stack":36,"description":49,"highlights":50,"run":55,"hue":56,"og":57,"shots":58,"case":66,"case_lang":5,"prev":90,"next":92},"api-gateway","Шлюз: API-gateway","backend",[37,38,39,40,41,42,43,44,45,46,47,48],"Go","stdlib only","Reverse proxy","Circuit breaker","Token bucket","SSE","Prometheus","slog","Vanilla JS","embed","Docker","GitHub Actions","API-шлюз на чистой stdlib: маршруты по хосту\u002Fпрефиксу из JSON-конфига с горячей перезагрузкой без обрыва соединений, балансировка round-robin и least-conn, health-checks, circuit breaker на узел, ретраи идемпотентных запросов, rate limit, хэшированные API-ключи, метрики Prometheus, живой дашборд на SSE.",[51,52,53,54],"Ноль зависимостей: только stdlib","Горячая перезагрузка конфига без обрыва соединений","Circuit breaker на узел и ретраи идемпотентных запросов","Нагрузочный тест: 4000 запросов, 40 клиентов (демо-замер)","cd api-gateway && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fgateway",200,"\u002Fog\u002Fapi-gateway.jpg",{"desktop":59,"desktop_sm":60,"mobile":61,"dw":62,"dh":63,"mw":64,"mh":65},"\u002Fshots\u002Fapi-gateway-desktop.webp","\u002Fshots\u002Fapi-gateway-desktop-sm.webp","\u002Fshots\u002Fapi-gateway-mobile.webp",1280,800,390,844,{"about":67,"features":71,"run":80,"notes":85},[68,69,70],"API-шлюз (обратный прокси) на чистой стандартной библиотеке Go: ни одной внешней зависимости. Маршруты по хосту и префиксу описываются JSON-конфигом, который перезагружается на лету без обрыва соединений.","За маршрутом стоит апстрим с несколькими узлами: балансировка round-robin или least-conn, health-checks, circuit breaker на каждый узел, ретраи идемпотентных запросов. Сверху rate limit, хэшированные API-ключи, метрики Prometheus и живой дашборд на SSE.","В демо-режиме внутри процесса поднимаются три фейковых апстрима и синтетический генератор трафика, который ходит через настоящий шлюз. Это не реальные сервисы и не реальная нагрузка.",[72,73,74,75,76,77,78,79],"Маршрутизация по хосту и самому длинному префиксу; неизвестный маршрут даёт 404, шлюз не open-proxy (адрес назначения берётся только из конфига)","Горячая перезагрузка: состояние неизменяемо и подменяется одним atomic.Pointer, слушающий сокет не трогается, ошибочный конфиг отклоняется целиком","Балансировка round-robin и least-conn только по узлам в состоянии healthy; health-loop на каждый узел","Circuit breaker на каждый узел (closed, open, half-open) и ретраи на другой узел для идемпотентных методов, тело буферизуется до 1 МиБ","Защита от SSRF: адреса апстримов проверяются при загрузке и ещё раз в момент соединения (DNS-rebinding)","Rate limit по token bucket (по IP или ключу), API-ключи хранятся как sha256, метрики Prometheus и дашборд на SSE на ванильном JS","Нагрузочный тест в наборе тестов: 4000 запросов, 40 клиентов, порядка 12000 запросов в секунду (демо-замер на одной машине, не бенчмарк)","Docker и GitHub Actions (go test -race в CI)",[81,82,83,84],"git clone \u003Cрепозиторий по запросу> api-gateway","cd api-gateway","SEED_DEMO=1 go run .\u002Fcmd\u002Fgateway   # прокси :8087, дашборд http:\u002F\u002F127.0.0.1:8088","go run .\u002Fcmd\u002Fgateway -genkey   # новый API-ключ и его sha256",[86,87,88,89],"Состояние rate limit и circuit breaker живёт в памяти одного процесса: при нескольких репликах лимиты не общие. Нет TLS на входе, счётчики не сохраняются при перезапуске.","WebSocket и Upgrade не поддерживаются (501); HTTP\u002F2 к апстримам и gRPC не проверялись. SIGHUP работает только на Unix, на Windows есть POST \u002Fapi\u002Freload.","Перцентили считаются по гистограмме с шагом ×1,25, погрешность до 25% вверх. Админка с одним токеном, без ролей и аудита.","Демо-ключ demo-key-not-a-secret публичен. Цифра о нагрузке получена на одной машине и не является бенчмарком.",{"slug":31,"title":91},"Комнаты: чат и доска в реальном времени",{"slug":93,"title":94},"notes-search","Картотека: заметки с поиском",1792697892698]