Настройка heartbeat (пульса) и механизма обнаружения разрыва соединения — это стандартная практика для обеспечения отказоустойчивости WebSocket. Основная цель: мгновенно фиксировать потерю связи с сервером, не дожидаясь таймаута на получение ответа от клиента, и автоматически переключаться на резервный источник (например, облачный API), если локальный узел недоступен.
Настройка таймаутов и обработка ошибок сети
Для корректного определения разрыва необходимо установить два типа таймаутов: интервал ожидания ответа на heartbeat (pingTimeout) и общий таймаут соединения (connectionTimeout). Если сервер не отвечает на ping в течение заданного времени (например, 3–5 секунд), клиент должен считать соединение нерабочим.
При возникновении сетевых ошибок (таймауты DNS, разрыв TCP-соединения) или получении кодов ошибок от сервера (502, 503, 408), клиент должен немедленно закрывать текущий WebSocket-канал и инициировать процедуру переключения на резервный источник. Не пытайтесь ждать ответа, который не придет — это потеря времени.
Важно обрабатывать не только явные ошибки сети, но и ситуации, когда сервер отвечает, но данные приходят с аномально большой задержкой. В таких случаях также стоит считать соединение нерабочим для текущей задачи.
Реализация механизма автоматического переключения (fallback)
Архитектура строится по принципу приоритизации: сначала локальный узел (Ollama на localhost:11434), затем облачный эндпоинт. В коде реализуйте функцию отправки запроса с параметром 'target', где по умолчанию значение — 'local'.
Если локальный узел недоступен или возвращает ошибку, функция должна автоматически переключать 'target' на облачный адрес. Это обеспечивает непрерывность работы даже при отсутствии интернета (если облако доступно) или при перегрузке локального железа.
Пример логики: если запрос к localhost:11434 не получил ответа за 5 секунд или вернул ошибку, следующий запрос отправляется на cloud-api.example.com. После успешного подключения к облаку можно попытаться снова установить связь с локальным узлом в фоне, но приоритет остается за облаком до восстановления локального сервиса.
Выбор стратегии повторных попыток
При потере соединения не стоит пытаться переподключаться с фиксированным интервалом (например, каждые 1 секунду), так как это создает нагрузку на сеть и сервер. Используйте экспоненциальную задержку: первая попытка через 1 сек, вторая через 2, третья через 4, четвертая через 8 и т.д., с максимальным лимитом (например, 30 секунд).
Если после нескольких попыток подключение не устанавливается, переходите к резервному источнику. Такая стратегия экономит ресурсы сети и позволяет избежать бесконечных циклов переподключения при временной недоступности сервера.
Использование heartbeat-сообщений
Сервер должен отправлять периодические сообщения (обычно каждые 15–30 секунд) для подтверждения активности. Клиент обязан отвечать на них (pong). Если клиент не отвечает, сервер закрывает соединение.
Клиент также может инициировать проверку живости: если прошло больше времени с последнего ответа от сервера, чем установленный таймаут heartbeat, клиент должен отправить ping и ждать pong. Отсутствие ответа означает разрыв.
Пример реализации на JavaScript (используя библиотеку ws или native API): установите интервал проверки каждые 20 секунд. Если ответ не пришел — закройте соединение и попробуйте переподключиться.
Обработка состояния 'stale connection'
Состояние 'stale connection' возникает, когда клиент продолжает использовать старый канал после того, как сервер его закрыл (например, из-за таймаута). Это приводит к тому, что сообщения отправляются в никуда.
Для предотвращения этого следите за статусом соединения. Если сервер вернул ошибку или закрыл канал, немедленно обновите состояние клиента на 'disconnected' и не пытайтесь отправить данные по старому каналу. Переподключение должно происходить только после получения подтверждения от сервера.
Настройка параметров безопасности
Обязательно используйте HTTPS для всех соединений, особенно при работе с облачными API. Проверьте валидность сертификатов на стороне клиента перед установкой соединения.
Настройте SameSite cookies как 'Strict' или 'Lax', чтобы избежать проблем с CORS и утечек данных. Это особенно важно, если вы используете локальный Ollama через прокси или туннель.
Мониторинг метрик качества соединения
Отслеживайте следующие метрики: время отклика (latency), количество ошибок сети, частоту потери heartbeat. Если задержка превышает норму (например, > 200 мс) или ошибки возникают чаще раза в час — это сигнал о проблемах с сетью или сервером.
Логирование этих метрик поможет выявить закономерности: например, если локальный узел работает медленно из-за нагрузки на CPU/GPU, а облачный API отвечает быстро, можно автоматически переключаться на облако при превышении порога задержки.
Тестирование отказоустойчивости
Протестируйте систему в условиях нестабильной сети: отключите интернет, имитируйте перегрузку локального сервера (запустите тяжелые вычисления), измените маршрутизацию. Убедитесь, что переключение на облачный источник происходит автоматически и без потери данных.
Проверьте, что при восстановлении локального узла система корректно возвращается к нему или продолжает использовать облако в зависимости от настроек приоритета.
Вопросы и ответы
Как определить, что heartbeat-сообщение потеряно?
Если клиент не получил ответ на отправленный ping в течение заданного таймаута (например, 5 секунд), соединение считается разорванным. Также следует учитывать общий таймаут ожидания ответа от сервера.
Можно ли использовать один таймаут для heartbeat и общего соединения?
Нет, лучше разделять: таймаут heartbeat должен быть короче (3–5 сек), а общий таймаут — больше (10–30 сек). Это позволяет быстрее реагировать на потерю связи без ложных срабатываний.
Что делать, если локальный узел работает медленно?
Если задержка превышает порог, система должна автоматически переключаться на облачный источник. Локальная модель может быть медленнее из-за ограничений железа, но она всегда доступна при наличии ресурсов.
По теме
// Пример реализации heartbeat и fallback
const ws = new WebSocket('wss://localhost:11434/v1/chat/completions');
let target = 'local';
let lastPong = Date.now();
const PING_INTERVAL = 20000; // каждые 20 сек
const PING_TIMEOUT = 5000; // 5 сек ожидания ответа
ws.onopen = () => {
console.log('Подключено к:', target);
};
ws.onmessage = (event) => {
lastPong = Date.now();
// обработка ответа...
};
ws.onclose = (code, reason) => {
if (target === 'local') {
target = 'cloud';
console.log('Переключение на облачный источник');
connectToCloud();
}
};
setInterval(() => {
ws.send(JSON.stringify({ type: 'ping' }));
}, PING_INTERVAL);
function checkHeartbeat() {
if (Date.now() - lastPong > PING_TIMEOUT) {
console.log('Heartbeat потерян, закрываем соединение');
ws.close();
}
}
setInterval(checkHeartbeat, 1000); // проверка каждую секунду


