Практика 2: TCP-шлюз XTP2 (сервис ключ-значение)
Говорим по XTP2 поверх TCP: KV-шлюз с сеансами, диспетчеризацией команд и серверным пушем — все концепции гл. 23 и гл. 33 сливаются здесь.
Обзор
46.1 Цель проекта
| Возможность | Дизайн |
|---|---|
| Набор команд | GET/SET/DEL/PING (REQUEST → RESPONSE, связка по id) |
| Пуш | при изменении key подписанным соединениям уходит EVENT (канал PUSH) |
| Сеансы | реестр соединений: обработчик + буфер остатков + множество подписок |
| Персистентность | KV в памяти + опциональный сброс на диск (паттерн гл. 32) |
| Оборона | xtp2 limits + idle_timeout + лимит частоты команд |
| Развёртывание | два эндпоинта: открытый текст 9000 + TLS 9443 |
46.2 Сеансы и диспетчеризация команд
static void DispatchMessage(XS_StreamConn* pConn, const xtp2_message* pMsg)
{
switch ( pMsg->command ) {
case CMD_GET: {
xtp2_span tKey = { pMsg->body, pMsg->body_size };
xbuffer* pVal = KV_Get(tKey);
ReplyResponse(pConn, pMsg->id, pVal ? 0 : 1, pVal); /* id возвращается */
break; }
case CMD_SET:
KV_Set(SplitKeyVal(pMsg)); /* body = key\0val конвенция */
NotifySubscribers(SplitKey(pMsg), pMsg); /* пуш EVENT */
ReplyResponse(pConn, pMsg->id, 0, NULL);
break;
default:
ReplyResponse(pConn, pMsg->id, 4096 + pMsg->command, NULL); /* неизвестная команда */
}
}
/* ReplyResponse: measure→encode→Send (паттерн упаковки гл. 33) */
Что только что произошло: поле команды (u16) становится стабильной осью диспетчеризации — новая команда = новый case и строка документации, а поверхность протокола (формат кадра) не шевелится. Пространство кодов ошибок тоже размечено (0 успех / 1 нет ключа / 4096+ уровень протокола) — «соглашение об ошибках первым» из первой практики действует и в бинарном мире.
46.3 Ответы и пуш
- Связка по id: RESPONSE дословно возвращает id REQUEST — клиент по нему сопоставляет асинхронные запросы (на одном соединении может висеть несколько в полёте)
- Пуш EVENT: множества подписок (по набору префиксов key на сеанс) обходятся после фиксации SET — дисциплина вещания списана с гл. 22 (обход под блокировкой, копирующая отправка, терпимость к отказам)
- Противодавление: оборона от шторма подписок — прикладное лимитирование (максимум EVENT на соединение в секунду) + деградация медленных — стратегическое противодавление гл. 22 перенесено на TCP
46.4 Оборона и два стека
- limits ужаты: потолок value вашего KV — это max_body; ставьте по бизнесу (например 256 КБ), не оставляйте умолчание 1 МиБ
- Битый кадр = разрыв + счёт: на SessionDrop наращивайте счётчик враждебных кадров в /stats (привычка наблюдаемости гл. 43)
- Уборка простоев: idle_timeout выметает зомби-соединения; множества подписок чистятся в терминале Close
- Два стека: открытый текст (отладка в интрасети) + TLS (наружу) на одном наборе колбэков (продукт упражнения 3 из гл. 23) — сертификаты SNI по гл. 12
46.5 Лист приёмки
| Измерение | Стандарт |
|---|---|
| Функции | четыре команды + пуш по подпискам; связка id верна |
| Устойчивость протокола | фрагментация/склейки/враждебные кадры — три класса тестов (упражнения гл. 33) |
| Конкурентность | 100 запросов в полёте на соединении под нагрузкой; вещание многим соединениям без перехлёстов |
| Жизненный цикл | после смены поколения подписки восстанавливаются сами (множество идёт в снимок Swap — ещё один образец «значения передаются») |
| Развёртывание | два эндпоинта + учения по горячей замене сертификатов |
Частые ошибки
- Симптом
- При высокочастотном SET горячего key отправочные буферы подписанных соединений распухают.
- Причина
- Отправка TCP — асинхронный буфер: без прикладного лимита EVENT и склейки (например, окно 50 мс сливает однотипные) медленные потребители утаскивают память. Копируйте стратегическое противодавление гл. 22.
- Симптом
- После горячей перезагрузки пуш умирает.
- Причина
- Подписки — «значения» (строки префиксов key): по критерию гл. 28 их надо передавать (снимок в Swap); реестр сеансов сам не передаётся (соединения принадлежат старому поколению), новые соединения подписываются заново.
Упражнения
По 46.2–46.5 соберите KV-шлюз (клиента можно написать вложенным TCC как тестовый конец).
Напишите минимальный клиентский модуль (connect → encode запроса → feed ответа → сопоставление id) для переиспользования другими скриптами.
Добавьте HTTP-сервис, переводящий REST-запросы в вызовы XTP2 (шлюз наоборот) — сотрудничество двух server (гл. 11).
Итоги
| Тезис | Вывод |
|---|---|
| Структура | реестр сеансов + таблица диспетчеризации + связка id |
| Пуш | канал EVENT переиспользует дисциплину вещания WS (лимит/склейка/деградация) |
| Оборона | limits по бизнесу + битый кадр = разрыв + уборка простоев |
| Поколения | подписки передаются (значения); таблица сеансов нет (соединения при поколении) |
- xrt · tcp — материал клиентских соединений (продвинутое упражнение)