安装与首次运行:构建、启动日志、第一个请求
从零到发出第一个请求只需要两样东西:一个 GCC 和一个 Python。本章所有日志与响应都是 demo-single 的实机运行结果——你可以逐字复现。
本章导读
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 仓库根目录:
$ build.bat # 最小变体:default $ build.bat sqlite xtp # 编入 SQLite 与 XTP2 扩展 $ build.bat --list-extensions 可选扩展: sqlite, xtp
Linux 下等价命令是 bash build.sh sqlite xtp。构建流水线做了五件事:
最后一步值得强调:链接成功才替换产物。构建失败时,release 目录里的 xs.exe 保持原样——这个"失败保护"有专门的测试用例看护,你在任何时候中断构建都不会得到一个坏二进制。
2.3 运行 demo 与日志解读
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 触发五阶段优雅停机 |
2.4 发起请求
另开一个终端,向 demo 发起几个请求(全部为实机输出):
$ 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_*,用了就编译失败。
- 数据线:demo 的 db/main.db 是真实数据文件;练习时随便改,重新构建仓库即可复原。
常见错误
- 症状
bind failed,进程退出;如果配了多个监听,之前已成功的端口也不会留下一半服务。- 原因
- 目标端口被占用。XServer 的装配是事务式的:任一监听失败,已绑定的监听会被完整回滚再退出——不会出现"半个服务在跑"的中间态。
./xs
bind failed
./xs /* 反复重试,不知道谁占了端口 */netstat -ano | findstr 9080
/* 改 xs.json 的 port,或结束占用进程 */- 症状
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编程练习
把 demo 的 xs.json 端口从 9080 改成 19080,重启并用 curl 验证。
http bound on 127.0.0.1:19080,curl /json 返回与本章一致的 JSON。用 build.bat sqlite 重建宿主,再运行 demo,确认 /api/list 正常;然后换回 default 变体运行,观察脚本编译报什么错。
在 demo 的 route.h 注册 /hello(GET),处理器返回你的名字。可以重启验证——热重载是卷四的事,现在先用"重启生效"的心智。
curl /hello 返回预期内容;日志出现 script loaded ... gen 2(重启也会累加代数吗?动手验证并在第 3 章开头找答案)。小结
| 要点 | 结论 |
|---|---|
| 环境要求 | GCC + Python 3,仅此而已 |
| 构建变体 | build.bat [sqlite] [xtp];链接成功才原子替换 |
| 启动顺序 | config → engine → script 编译 → ServiceInit → bind → running |
| 日志关键行 | gen N(代)、workers=N、bound、[app 前缀] 行来自你的脚本 |
| 配置查找 | 默认在 exe 所在目录找 xs.json;可用参数显式指定 |
- xrt 教程 · 开发环境 — 宿主构建所用的 GCC 工具链细节
- xrt 教程 · 内存调试 — 排错期的底层武器(第 6 章会回到这个话题)