Мультисервисность и топология: один процесс — целая карта сервисов
В services[] не обязан быть один элемент. Реальные развёртывания часто смешивают «ферму HTTP-сайтов + WebSocket-push + TCP-шлюз + UDP-сердцебиение» — всё на одном движке, одном наборе скриптовых возможностей и одной корректной остановке.
Обзор
11.1 Введение
Наивный путь мультиервисного развёртывания — «по процессу на тип» — оборачивается расползанием процессов, хаосом планирования портов и разбросанными логами. Топологическая модель XServer — много server в одном процессе: один xs.json описывает все эндпоинты, один процесс несёт все сервисы — с общим пулом потоков движка, общими скриптовыми возможностями и единой корректной остановкой. Продакшн-репозиторий home работает именно так: один процесс XServer, 14 vhost, смешаны http и статические сайты.
11.2 Один процесс, много протоколов
{
"engine": { "workers": 8 },
"services": [
{ "class": "http", "name": "web", "port": 80, "tls": true, "port_tls": 443,
"host_default": { "name": "app", "path": "wwwroot", "devlang": "c", "devfile": "main.c" } },
{ "class": "ws", "name": "push", "port": 8081,
"host_default": { "name": "app", "devlang": "c", "devfile": "push_main.c" } },
{ "class": "tcp", "name": "gw", "port": 9000, "tls": true, "port_tls": 9443,
"devlang": "c", "devfile": "gw_main.c" },
{ "class": "udp", "name": "beat", "port": 9010,
"devlang": "c", "devfile": "beat_main.c" }
]
}
Что только что произошло: четыре сервиса, четыре скрипта; при сборке все кандидаты обязаны скомпилироваться до первого bind — любая неудача компиляции отвергает весь старт (та же дисциплина, что «сначала компиляция, потом замена» у горячей перезагрузки). Каждый сервис меняет поколения независимо (reload публикуется на уровне server), не мешая остальным.
11.3 engine.workers
- Единственная ручка уровня процесса: 0–1024, 0 = умолчание xrt (на этой машине в демо — 12)
- Колбэки всех server делят один пул — HTTP-запросы, WS-сообщения, TCP-данные и UDP-дейтаграммы исполняются на workers
- Неизменяемо при горячей перезагрузке: намерение reload, меняющее workers, отклоняется (число потоков — факт уровня процесса)
- Оценка ёмкости: колбэки — короткая событийная обработка; разумный старт — workers ≈ числу ядер CPU; долгие блокирующие задачи уходят в отдельный поток (глава 36), а не в наращивание workers
11.4 Запросы топологии в рантайме
/* перечисляем все сервисы из скрипта сервиса push */
static bool PrintTopo(XS_ServerInfo* pServer, void* pUserData)
{
printf("- %s (%s) :%u%s\n", pServer->Name, pServer->Class,
pServer->Port, pServer->TLS ? " +tls" : "");
return true;
}
xsEnumServers(PrintTopo, NULL);
/* берём retained lease по имени (обязателен Release) */
XS_ServerInfo* pWeb = xsServerFind("web");
if ( pWeb != NULL ) {
XS_HostInfo* pHost = xsHostFind(pWeb, "app");
/* ... читаем топологическую информацию между сервисами ... */
xsServerRelease(pWeb);
}
Сторона чтения топологии защищена RWLock (много чтений, мало записей), путь Accept каждый раз входит в читающую сторону — запросы сами по себе дёшевы. Аргументы перебора заимствуются внутри колбэка; дисциплина lease — в справочнике.
11.5 Контракт
- Сборка: при мультисервисности все кандидаты компилируются до bind; любая неудача отвергает весь старт.
- Смена поколений: reload публикуется на уровне отдельного server; xsReloadAllSubmit — пакетная транзакция (всё или откат всего).
- Запросы: xsServerFind возвращает retained lease; аргументы перебора заимствованы на время колбэка — перед сохранением Retain.
Частые ошибки
- Симптом
- Намерение reload FAILED; Message сообщает, что workers неизменяем.
- Причина
- Пул потоков — ресурс уровня процесса; пересоздание в рантайме изменило бы конкурентную семантику всех колбэков — явно отклоняется, а не рискует.
- Симптом
- После перезагрузки сервиса web закэшированный в push XS_ServerInfo* становится дикой ссылкой.
- Причина
- Указатели из колбэков перебора заимствуются на один раунд; сохранение между поколениями/сервисами требует Retain в lease.
Упражнения
Запустите топологию 11.2 (скрипты могут ничего не экспортировать) и составьте таблицу топологии по логу старта.
Добавьте в web-сервис маршрут /topo и выводите JSON-топологию через xsEnumServers.
Намеренно сломайте компиляцию скрипта gw и запустите; затем почините gw и сломайте скрипт web.
Итоги
| Тезис | Вывод |
|---|---|
| Топология | Много server в процессе, пять протоколов вперемешку, общий движок и остановка |
| Сборка | Все кандидаты компилируются до bind; любая неудача отвергает старт целиком |
| workers | Уровень процесса, общий для колбэков, неизменяем при reload; старт ≈ ядрам CPU |
| Запросы | xsServerFind (lease) / xsEnumServers (заимствование) |
- справочник xrt · executor — модель вытеснения пула workers