惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
MyScale Blog
MyScale Blog
Microsoft Security Blog
Microsoft Security Blog
量子位
B
Blog
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
T
The Exploit Database - CXSecurity.com
N
News | PayPal Newsroom
Cloudbric
Cloudbric
A
About on SuperTechFans
AI
AI
Hacker News: Ask HN
Hacker News: Ask HN
S
Schneier on Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 最新话题
T
The Blog of Author Tim Ferriss
Simon Willison's Weblog
Simon Willison's Weblog
有赞技术团队
有赞技术团队
H
Heimdal Security Blog
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
D
Docker
Security Archives - TechRepublic
Security Archives - TechRepublic
N
News and Events Feed by Topic
IT之家
IT之家
Know Your Adversary
Know Your Adversary
N
Netflix TechBlog - Medium
T
Tailwind CSS Blog
B
Blog RSS Feed
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
博客园 - 叶小钗
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LangChain Blog
The Hacker News
The Hacker News
Y
Y Combinator Blog
I
Intezer
The Register - Security
The Register - Security
F
Full Disclosure
V
V2EX
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Last Week in AI
Last Week in AI
Martin Fowler
Martin Fowler

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Почему ваш Go‑сервис ломается под 1000 RPS и как найти узкое место за полчаса
badcasedaily · 2026-05-11 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение6 мин

Охват и читатели921

Туториал

Go‑сервис на малых нагрузках работает идеально. Горутины дешёвые, GC быстрый, net/http из коробки тянет приличный трафик. Разработчик прогоняет функциональные тесты, видит зелёное, деплоит. Приходят 1000 RPS, и latency p99 взлетает с 50ms до 5 секунд, в логах начинают мелькать таймауты, а в Grafana рисуется красивая кривая деградации.

Инструменты: vegeta и wrk2

Для нагрузочного тестирования Go‑сервисов используем два инструмента.

vegeta написан на Go, понимает гошные паттерны, выводит результаты в удобном формате:

go install github.com/tsenart/vegeta@latest

echo "GET http://localhost:8080/api/orders" | \
  vegeta attack -rate=500/s -duration=30s | \
  vegeta report

wrk2 — форк wrk с фиксированной частотой запросов. Обычный wrk отправляет запросы настолько быстро, насколько может: если сервер замедлился, wrk тоже замедляется, и вы не видите реальную деградацию. wrk2 продолжает слать с заданной частотой, и если сервер не успевает, это видно по latency:

wrk2 -t2 -c10 -d30s -R1000 http://localhost:8080/api/orders

Начинайте с малого: 100 RPS, потом 300, потом 500, потом 1000. На каждом шаге смотрите на три вещи.

Что смотреть в результатах

После прогона vegeta выдаёт что‑то такое:

Requests      [total, rate, throughput]  30000, 1000.03, 987.21
Duration      [total, attack, wait]     30.412s, 29.999s, 412.912ms
Latencies     [min, mean, 50, 90, 95, 99, max]
              1.2ms, 45.3ms, 12.1ms, 89.4ms, 234.5ms, 2134.1ms, 5312.7ms
Status Codes  [code:count]  200:29847  503:112  0:41
  • p50 vs p99. p50 = 12ms, p99 = 2134ms. Медианный запрос быстрый, но каждый сотый обрабатывается в 175 раз дольше. При 1000 RPS это 10 человек в секунду, которые ждут по две секунды.

  • throughput vs rate. Просили 1000 RPS, throughput 987. 13 запросов в секунду теряются. Сервис на пределе.

  • Status codes. 112 ошибок 503, 41 ошибка с кодом 0 (таймаут, сервер не ответил). 0.5% ошибок за 30 секунд — тысячи в час на реальном трафике.

Разрыв между p50 и p99 — главный индикатор. Если p50 и p99 близки, сервис стабилен. Если p99 в десятки раз больше, где‑то есть ресурс, который при конкурентном доступе деградирует.

Причина 1: пул коннектов к базе данных

По дефолту sql.DB в Go не ограничивает количество открытых соединений (MaxOpenConns = 0) и держит всего 2 idle‑соединения. При 1000 RPS каждый запрос может открыть новое соединение к базе (TCP handshake + TLS + аутентификация), Postgres захлёбывается от количества процессов, p99 взлетает.

db, _ := sql.Open("postgres", connStr)

db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(3 * time.Minute)

Мониторьте пул:

func reportDBStats(db *sql.DB) {
    ticker := time.NewTicker(10 * time.Second)
    for range ticker.C {
        stats := db.Stats()
        log.Printf("db: open=%d inuse=%d idle=%d wait=%d wait_dur=%s",
            stats.OpenConnections, stats.InUse, stats.Idle,
            stats.WaitCount, stats.WaitDuration)
    }
}

Если WaitCount растёт, пул мал. Если InUse постоянно на максимуме, вы на пределе.

Причина 2: HTTP‑клиент с дефолтами

http.DefaultClient использует DefaultTransport с MaxIdleConnsPerHost = 2. Два. При 1000 RPS к одному downstream вы постоянно открываете и закрываете TCP‑соединения.

var paymentClient = &http.Client{
    Timeout: 5 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:          100,
        MaxIdleConnsPerHost:   100,
        MaxConnsPerHost:       100,
        IdleConnTimeout:       90 * time.Second,
        TLSHandshakeTimeout:  5 * time.Second,
    },
}

Используйте один клиент на весь сервис. Новый http.Client = новый пул соединений = все старые выброшены.

Проверка:

watch -n1 "ss -tn state time-wait | grep :8081 | wc -l"

Сотни TIME_WAIT — соединения создаются и закрываются вместо переиспользования. После настройки MaxIdleConnsPerHost TIME_WAIT уйдут.

Причина 3: горутины без ограничений

net/http стартует горутину на каждый запрос. При 1000 RPS и обработке по 100ms живёт ~100 горутин. Если обработка замедлилась до секунды, горутин уже 1000, каждая держит стек, буферы, соединения. Дальше цепная реакция: больше горутин, больше памяти, чаще GC, медленнее, ещё больше горутин и OOM.

var sem = make(chan struct{}, 200)

func rateLimitMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        select {
        case sem <- struct{}{}:
            defer func() { <-sem }()
            next.ServeHTTP(w, r)
        default:
            http.Error(w, "too many requests", http.StatusServiceUnavailable)
        }
    })
}

Мониторинг:

func reportGoroutines() {
    ticker := time.NewTicker(10 * time.Second)
    for range ticker.C {
        log.Printf("goroutines: %d", runtime.NumGoroutine())
    }
}

Если число растёт и не возвращается к baseline после спада нагрузки — утечка. Обычно это горутина, заблокированная на чтении из канала или на HTTP‑запросе без таймаута.

Причина 4: GC под нагрузкой

Если сервис аллоцирует много короткоживущих объектов (десериализация JSON, создание буферов), частота GC растёт и на p99 видны всплески.

GODEBUG=gctrace=1 ./myservice 2>&1 | head -20

GC каждые 10–20ms — слишком много аллокаций. Решения:

var bufPool = sync.Pool{
    New: func() any { return make([]byte, 0, 4096) },
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
    buf := bufPool.Get().([]byte)
    buf = buf[:0]
    defer bufPool.Put(buf)
    // используем buf вместо нового слайса
}

И быстрая десериализация через json‑iter вместо encoding/json:

import jsoniter "github.com/json-iterator/go"
var json = jsoniter.ConfigCompatibleWithStandardLibrary

Профилирование аллокаций:

go tool pprof -alloc_objects http://localhost:6060/debug/pprof/heap

Обычно топ-3 функции отвечают за 80% аллокаций.

Причина 5: отсутствие таймаутов

http.Server по умолчанию не имеет таймаутов. Медленный клиент держит соединение бесконечно.

server := &http.Server{
    Addr:         ":8080",
    Handler:      mux,
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 10 * time.Second,
    IdleTimeout:  120 * time.Second,
}

На стороне downstream — context с таймаутом:

func handleOrders(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(r.Context(), 4*time.Second)
    defer cancel()

    rows, err := db.QueryContext(ctx, "SELECT * FROM orders WHERE user_id=$1", uid)
    if err != nil {
        http.Error(w, "timeout", http.StatusGatewayTimeout)
        return
    }
    
    req, _ := http.NewRequestWithContext(ctx, "GET", "http://inventory/check", nil)
    resp, err := paymentClient.Do(req)
    // ...
}

Без таймаутов один зависший downstream тянет за собой весь сервис: горутины копятся, пул забивается, новые запросы встают в очередь.

Тестирование цепочки сервисов

Для цепочки нужны метрики на каждом сервисе:

var requestDuration = promauto.NewHistogramVec(
    prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10},
    },
    []string{"method", "path", "status"},
)

var activeRequests = promauto.NewGauge(prometheus.GaugeOpts{
    Name: "http_active_requests",
})

Создаёте нагрузку на точку входа и в Grafana смотрите, какой сервис деградирует первым. Тот, у которого http_request_duration_seconds растёт раньше остальных, обычно и есть узкое место.

Порядок действий

Когда сервис деградирует под нагрузкой, проверяйте по списку:

  1. Прогоните vegeta на 100 → 300 → 500 → 1000 RPS, найдите точку деградации

  2. db.Stats() — WaitCount растёт? Пул мал

  3. ss -tn state time-wait — TIME_WAIT много? HTTP‑клиент не переиспользует соединения

  4. runtime.NumGoroutine() — растёт и не падает? Утечка

  5. GODEBUG=gctrace=1 — GC каждые 10ms? Слишком много аллокаций

  6. Таймауты на сервере и клиенте есть? Если нет, один зависший запрос тянет всё

На каждом шаге: исправил, прогнали нагрузку, сравнили p99. Упал — нашли причину. Не упал — следующий пункт.

Если смотреть шире, такие проверки нужны не только для тушения пожаров. Они помогают понять, где в архитектуре появляются хрупкие места: слишком тесная связка с базой, отсутствие backpressure, неограниченные горутины, downstream без таймаутов, сервисы без нормальной наблюдаемости. В микросервисной системе всё это быстро перестаёт быть локальной проблемой одного endpoint«а.»

Поэтому при разработке сервисов на Go важно думать не только о коде обработчика, но и о поведении всей цепочки под нагрузкой. Эти темы — от взаимодействия сервисов до отказоустойчивости и observability — подробно разбираются в рамках курса «Микросервисы на Go», где Go рассматривается уже как инструмент для построения production‑систем, а не просто быстрых HTTP‑сервисов.

А если хочется зайти в тему с архитектурной стороны, начните с бесплатного демо-урока 19 мая в 20:00 — «Грамотная декомпозиция монолита: когда микросервисы не нужны». На нем можно будет познакомиться с преподавателем-практиком, посмотреть на формат обучения и задать вопросы по декомпозиции, архитектурным решениям и курсу. Записаться