Глава 2 / 51
Том 1 · Старт и ментальная модель

Установка и первый запуск: сборка, лог старта, первый запрос

От нуля до первого запроса нужно ровно две вещи: GCC и Python. Каждая строка лога и каждый ответ в этой главе — реальный вывод demo-single, его можно воспроизвести дословно.

Обзор

Позиционирование: цель главы — не «установить», а научиться читать лог старта — это ваше главное вещественное доказательство при отладке (глава 6 строится именно на этом навыке). Если вы уже собирали хост — переходите сразу к 2.3.

2.1 Введение

При развёртывании нового серверного стека мучительное — обычно не написание кода, а подготовка среды: Node требует nvm и node_modules, Go — toolchain и GOPATH, классическому C-сервису нужен собственный конвейер сборки. Ответ XServer — свернуть всё это в этап сборки и внутрь бинарника: при сборке ваш GCC создаёт xs.exe, одновременно TCC-компилятор, полный набор заголовков SDK и определения системных библиотек пакуются со сжатием LZMA внутрь бинарника; в рантайме на целевой машине устанавливать не нужно ничего.

Так что «установка XServer» — это два действия: один раз собрать хост, затем запустить готовый каталог приложения (demo-single). С них и начнём.

2.2 Сборка хоста

Предусловий всего два: GCC (выбирается через --cc) и Python 3. В корне репозитория xserver:

Терминал · Windows
$ build.bat                # минимальный вариант: default
$ build.bat sqlite xtp     # вкомпилировать расширения SQLite и XTP2
$ build.bat --list-extensions
доступные расширения: sqlite, xtp

Эквивалент для Linux — bash build.sh sqlite xtp. Конвейер сборки делает пять вещей:

собрать LZMA-упаковщик │ ▼ сгенерировать ресурсы VFS (заголовки SDK + include TCC + .def системных библиотек → массивы байт на C) │ ▼ скомпилировать объекты расширений (-DXS_USE_SQLITE / XS_USE_XTP2, по варианту) │ ▼ скомпилировать хост (main.c + libtcc + VFS + ресурсы) │ ▼ пробная линковка во временном каталоге → при успехе атомарная замена xs.exe ← провал всегда сохраняет последнюю рабочую версию

Последний шаг стоит подчеркнуть: артефакт заменяется только после успешной линковки. При неудачной сборке xs.exe в каталоге release остаётся нетронутым — эту «защиту от сбоя» охраняет отдельный тест-кейс; прерывание сборки в любой момент не оставит битый бинарник.

2.3 Запуск демо и разбор лога

demo-single — полное демонстрационное приложение из репозитория (маршрутизация, шаблоны, SQLite, контролируемые страницы). Зайдите в каталог и запустите:

Терминал · реальный вывод
$ cd release/demo-single
$ ./xs.exe
[xs] config loaded: D:\GIT\xserver\release\demo-single/xs.json (1 servers)
[xs] server 'demo' class=http 127.0.0.1:9080 tls=off enabled
[xs]   default-host 'app' path=...\wwwroot devfile=...\main.c
[xs] engine started, workers=12
[xs] script loaded: ...\main.c (2.6 KB gen 1)
[demo] database opened: ...\db\main.db
[xs] server 'demo' http bound on 127.0.0.1:9080
[xs] running, press Ctrl+C to stop

Что только что произошло — построчно:

Строка логаСмыслНа что смотреть
config loaded (1 servers)xs.json разобранfail-fast: недопустимое поле завершит процесс здесь же с именем поля
server 'demo' class=http ...эхо конфигурации сервисапоследний взгляд на адрес / порт / TLS
engine started, workers=12движок конкурентности готовчисло worker из engine.workers, 0 = значение xrt по умолчанию (здесь 12)
script loaded (2.6 KB gen 1)компиляция TCC завершеназаметьте gen 1 — это «поколение», герой главы 3
[demo] database openedваш ServiceInit работаетстроки с префиксом [demo] пишет сам скрипт приложения
http bound on 127.0.0.1:9080прослушивание включенопри неудачном bind все уже занятые порты откатываются
running, press Ctrl+C to stopрежим обслуживанияCtrl+C / SIGTERM запускает пятифазную корректную остановку
Обратите внимание на семантику порядка: порты занимаются только после компиляции скрипта. Это гарантирует, что к моменту внешней видимости приложение полностью инициализировано — та же дисциплина, что и «сначала компиляция, потом замена» в горячей перезагрузке (том 4).

2.4 Отправка запросов

Откройте второй терминал и отправьте демо несколько запросов (весь вывод реальный):

Терминал · реальный вывод
$ curl -s http://127.0.0.1:9080/json
{"server":"xs3 demo","ok":true,"host":"app"}

$ curl -s http://127.0.0.1:9080/text
demo-single ok (from includes/demo_extra.h)

$ curl -s http://127.0.0.1:9080/api/v1/items/42
{"id":42,"name":"bench-write","created":1788494894}

$ curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9080/nope
404
$ curl -sI http://127.0.0.1:9080/json
HTTP/1.1 405 Method Not Allowed
Allow: GET

Что только что произошло:

  • /json прошёл через Handle_JSON в скрипте приложения — построение объекта через xvalue и сериализация (тема главы 18);
  • /api/v1/items/42 попал в динамический маршрут /api/v1/items/{id}; захваченный сегмент 42 искался в SQLite (глава 17);
  • /nope не совпал ни с одним маршрутом и обработчиком → 404;
  • HEAD /json получил 405 + Allow: GET — в маске методов маршрута зарегистрирован только GET, и HEAD был отвергнут в слоте метода до обработчика. Реальная деталь маршрутизации; глава 17 расскажет, как обрабатывать HEAD.

2.5 Контракт

  • Потоки: число workers фиксируется при старте и неизменно при горячей перезагрузке (изменения отклоняются).
  • Варианты: вариант сборки определяет доступные скрипту символы — в варианте default нет sqlite3_* / xtp2_*; их использование ломает компиляцию.
  • Данные: db/main.db демо — настоящий файл данных; экспериментируйте смело, пересборка репозитория его восстановит.

Частые ошибки

Порт занят, старт не удался
Симптом
bind failed, процесс завершается; при нескольких слушателях ранее занятые порты тоже не остаются обслуживаться наполовину.
Причина
Целевой порт занят. Сборка XServer транзакционна: при отказе любого слушателя уже занятые полностью откатываются перед выходом — промежуточного состояния «половина сервиса работает» не бывает.
✗ Слепие ретраи
./xs
bind failed
./xs          /* снова и снова, не зная, кто держит порт */
✓ Сначала проверить, потом менять
netstat -ano | findstr 9080
/* сменить port в xs.json или завершить процесс-владелец */
Двойной клик по xs.exe не в том каталоге
Симптом
config load failed — хотя вы точно знаете, что xs.json лежит в каком-то каталоге.
Причина
Без аргументов XServer ищет xs.json в каталоге исполняемого файла, а не в текущем рабочем. Скопируйте xs.exe куда-то отдельно и щёлкните — приложение он, конечно, не найдёт.
✗ Ошибочно
copy xs.exe C:\tools\
C:\tools\xs.exe      /* config load failed */
✓ Правильно
/* xs.json и каталог исходников рядом с exe;
 * или явно указать путь к конфигу */
./xs D:\apps\myapp\xs.json

Упражнения

БазовыйСмена порта и рестарт

Смените порт xs.json демо с 9080 на 19080, перезапустите и проверьте curl'ом.

Приёмка: в логе есть http bound on 127.0.0.1:19080, а curl /json возвращает тот же JSON, что в главе.
ПродвинутыйСмена варианта

Пересоберите хост командой build.bat sqlite, запустите демо и убедитесь, что /api/list работает; затем верните вариант default и посмотрите, какую ошибку даст компиляция скрипта.

Приёмка: вы можете объяснить, «почему демо не стартует на варианте default», и на каком этапе происходит ошибка (подсказка: сверьте с порядком лога из 2.3).
ВызовСвой маршрут

Зарегистрируйте в route.h демо маршрут /hello (GET), обработчик возвращает ваше имя. Проверяйте рестартом — горячая перезагрузка подождёт том 4; пока живём в модели «рестарт применяет».

Приёмка: curl /hello возвращает ожидаемое; в логе видно script loaded ... gen 2 (рестарт увеличивает счётчик поколений? Проверьте и найдите ответ в начале главы 3).

Итоги

ТезисВывод
Требования к средеGCC + Python 3, и всё
Варианты сборкиbuild.bat [sqlite] [xtp]; атомарная замена только при успешной линковке
Порядок стартаconfig → engine → компиляция скрипта → ServiceInit → bind → running
Ключевые строки логаgen N (поколение), workers=N, bound; строки с префиксом приложения — из вашего скрипта
Поиск конфигурацииxs.json ищется рядом с exe по умолчанию; путь можно передать явно
Что дальше по xrt