/
inssane
/
Axios-api
Обзор
Документация
Войти
/
inssane
/
Axios-api
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
api/interceptors/keepAlive.ts
92 строки
5 KB
Павел Чернышев
feat(keepalive): сменить дефолт scheduling на fifo для pod-to-pod
02 июн 2026, 18:47
02 июн 2026, 18:47
21e55f8
Код
Авторство
О чём код?
import type { Agent as HttpAgent, AgentOptions } from 'node:http'; import type { Agent as HttpsAgent } from 'node:https'; import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios'; import { defineMiddleware } from '../core/types'; export interface KeepAliveOptions { /** Макс. одновременных сокетов на хост. */ maxSockets?: number; /** Макс. простаивающих сокетов на хост, удерживаемых открытыми. */ maxFreeSockets?: number; /** Таймаут неактивного сокета, мс. */ timeout?: number; /** Начальная задержка перед keep-alive пакетами, мс. */ keepAliveMsecs?: number; /** Макс. всего сокетов на агента. */ maxTotalSockets?: number; /** Порядок переиспользования сокетов из пула. */ scheduling?: 'fifo' | 'lifo'; } /** * Дефолты под SSR-под (1 Node-процесс на под, за балансером и инфраструктурой). * Масштабирование — горизонтальное подами, поэтому пул держим умеренным: суммарная * нагрузка на бэкенд = пул × число подов. Все значения переопределяются опциями. * * - `maxSockets: 64` — потолок параллельных соединений на хост-бэкенд с одного пода; * один event-loop не утилизирует больше с пользой, а потолок защищает бэкенды. * - `maxFreeSockets: 32` — рабочий пул простаивающих сокетов для переиспользования. * - `timeout: 30_000` — клиент закрывает неактивный сокет через 30 с. ВАЖНО: значение * должно быть МЕНЬШЕ idle-таймаута самого «короткого» посредника в цепочке (балансер, * mesh, nginx — обычно 60 с+), иначе переиспользование уже закрытого ими соединения * даёт спорадические ECONNRESET. * - `keepAliveMsecs: 1_000` — TCP keep-alive пробы раз в секунду: вовремя обнаружить * мёртвого пира. * - `scheduling: 'fifo'` — крутим запросы по всем сокетам пула равномерно. При походах * под-в-под через k8s Service (L4-балансировка kube-proxy) соединение прибито к одному * поду на всю жизнь, поэтому `fifo` держит больше живых соединений и ровнее размазывает * нагрузку по подам бэкенда, чем `lifo` (тот концентрируется на горячем наборе). * За балансером, гасящим idle-соединения, осмысленнее `lifo` — тогда переопределите. */ const SSR_DEFAULTS: Required< Pick<KeepAliveOptions, 'maxSockets' | 'maxFreeSockets' | 'timeout' | 'keepAliveMsecs' | 'scheduling'> > = { maxSockets: 64, maxFreeSockets: 32, timeout: 30_000, keepAliveMsecs: 1_000, scheduling: 'fifo' }; /** * Переиспользование TCP-соединений (HTTP keep-alive) для серверного рантайма. * * `runtime: 'server'` — в браузере билдер пропускает мидлвару (там axios ходит через * XHR/fetch и агенты не нужны). `node:http`/`node:https` загружаются динамически внутри * `apply`, поэтому не попадают в браузерный бандл. Агенты создаются один раз и * переиспользуются между запросами. */ export const keepAliveMiddleware = defineMiddleware<KeepAliveOptions>({ name: 'keep-alive', runtime: 'server', apply(instance: AxiosInstance, options: KeepAliveOptions = {}): void { const agentOptions: AgentOptions = { keepAlive: true, ...SSR_DEFAULTS, ...options }; let agents: Promise<{ httpAgent: HttpAgent; httpsAgent: HttpsAgent; }> | null = null; const loadAgents = () => { if (!agents) { agents = Promise.all([import('node:http'), import('node:https')]).then( ([http, https]) => ({ httpAgent: new http.Agent(agentOptions), httpsAgent: new https.Agent(agentOptions) }) ); } return agents; }; instance.interceptors.request.use( async (config: InternalAxiosRequestConfig) => { const { httpAgent, httpsAgent } = await loadAgents(); config.httpAgent = httpAgent; config.httpsAgent = httpsAgent; return config; } ); } });