Address 架构拆解:Astro、Hono、PostgreSQL 与同步服务如何在一个 Docker 文件里协作
【免费下载链接】addressA self-hosted address and synthetic test-profile generator for 27 countries and regions, built from real open-data streets, administrative areas, coordinates, and postcodes. Supports multilingual output, IP-nearby generation, map previews, and API access. 基于真实开放数据的自托管地址与合成测试资料生成器,覆盖 27 个国家和地区,支持多语言地址、IP 附近生成、地图预览与 API 调用项目地址: https://gitcode.com/gh_mirrors/address4/address
本文带你完整拆解 Address 自托管地址生成器的技术架构:这是一个基于真实开放数据(街道、行政区、坐标、邮编)构建的地址与合成测试资料生成器,覆盖 27 个国家与地区。它最巧妙的设计是——前端 Astro、API 框架 Hono、数据库 PostgreSQL、后台同步服务,全部打包进同一个 Docker 镜像,再由 docker-compose 拆分成 6 个协作的服务。下面我们就一层层拆开看。
一、先看全局:一套系统,六个角色
Address 的架构可以用一句话概括:一份代码、一个镜像、六个容器。docker-compose.yml定义了这些服务:
| 服务 | 角色 | 一句话说明 |
|---|---|---|
bootstrap | 一次性初始化 | 启动前生成并落地所有密钥文件 |
postgres | 数据底座 | PostgreSQL 16,唯一的持久化存储 |
migrate | 一次性迁移 | 数据库结构升级,跑完即退出 |
api | 门面 | Hono 服务,对外提供 API 和网页 |
sync | 数据工厂 | 定时拉取开放数据、跑 ETL、发布地址池 |
credential-broker | 密钥管家 | 统一管理第三方 API Key 的加密与调用 |
它们通过两条内部网络协作:internal(内网隔离)和egress(对外出口)。只有api把端口映射到宿主机(默认127.0.0.1:8787),其余服务全部藏在容器网络里,天然安全。
二、一个 Dockerfile 如何装下"前端 + API + Python ETL"
很多自托管项目要么镜像臃肿、要么每个服务单独打包。Address 的 Dockerfile 用多阶段构建解决了两难:
1. build 阶段:只干构建这一件事
FROM node:24-bookworm AS build npm ci # 安装依赖 npm run build # 执行 Astro 构建,产出 dist/Astro 采用output: 'static'纯静态模式(见 astro.config.mjs),配合 React 组件渲染页面,构建产物就是一堆 HTML/CSS/JS 文件。
2. runtime 阶段:一个"全能"运行环境
runtime 阶段做了一件不寻常的事——给 Node 项目装 Python 虚拟环境:
apt-get install python3 python3-pip python3-venv python3 -m venv /srv/address/venv pip install -r server/sync/requirements.txt为什么?因为sync同步服务要解析 Geofabrik 的 PBF 地图文件,依赖 pyosmium 这类 Python/C++ 库。把 Python 环境提前烤进镜像,同步服务开箱即用。
同时它还内置了三个安全细节:
- 低权限用户:创建非 root 的
address用户,入口脚本用gosu降权执行; - 密钥不落镜像:密码一律通过
*_FILE环境变量指向挂载的密钥文件,由 ops/container-entrypoint.sh 在启动时读入; - 数据目录预置:
data/staging(同步暂存区)、runtime/sync-control(调度状态)提前创建并授权。
build 阶段 ──► 产出 dist/ 静态文件 ──► runtime 阶段 ├─ Python venv(sync 用) ├─ gosu + 低权限用户 └─ entrypoint 读密钥文件 ──► 降权启动三、Hono:一个进程同时当"API 网关 + 静态服务器 + 反向代理"
生产环境没有 Nginx——server/api/server.ts 里的 Hono 实例身兼三职:
路由分发:一个 fetch 函数管全部请求
启动后按路径前缀把请求分流:
| 路径前缀 | 去向 | 认证方式 |
|---|---|---|
/api/v1/* | Hono 主 App(公开 API) | API Token |
/admin/api/* | 管理后台 API | 管理员会话 |
/web-api/v1/* | 转发到主 App | Web 登录态 |
/sync-control/* | 反向代理到 sync 服务 | 默认不公开 |
| 其他 | 静态文件(serveStatic) | 未登录跳转登录页 |
这个设计最妙的地方在于:Astro 构建出的静态页面,由 Hono 用serveStatic直接吐出,未登录用户访问任何页面都会被 302 重定向到多语言登录页。前端、API、鉴权在一个进程里闭环。
内置限流:生成接口不裸奔
地址生成是 CPU 和数据库密集型操作,所以代码里用InFlightLimiter给生成类接口加了并发闸——超限直接返回429 + Retry-After,防止并发风暴打垮数据库连接池。
四、PostgreSQL:一个库,两个"分身"
数据层是 Address 架构的定海神针。server/database/runtime.ts 启动时打开同一个 PostgreSQL 的两个逻辑连接:
- address 库视角:地址池、行政目录、位置坐标——生成器真正吐出的数据;
- control 库视角:管理员账号、API Token、服务商凭据、同步配置——系统自身的控制面。
两者共用一个物理库但职责分明,带来两个关键收益:
① 原子发布:sync服务对每个国家维护"候选区 → 校验 → 事务替换线上数据"的流程。新批次地址先在候选表校验质量,通过后在单个国家事务中切换 active 指针;中途失败,旧数据分毫不动。
② 松耦合的后台:sync 进程把队列快照写进数据库,管理后台直接读库展示状态——即使同步进程正忙于导入日本的全量数据(实测峰值 6.5 GB 内存),仪表盘依然秒开。
五、sync 服务:镜像里最"重"的后台工人
sync 服务是整套系统的"数据工厂",入口在 server/sync/index.mjs,它同时干四件事:
- 调度器:
SYNC_SCHEDULER_ENABLED=true时自动补跑未完成的国家初始化,每天定时重算执行资格; - ETL 管道:通过 DuckDB 读 Overture GeoParquet、用 pyosmium 流式解析 Geofabrik PBF,机构过滤、去重、住宅证据校验后写入;
- 发布校验:publication-validation worker 每秒扫描发布批次,不合格的国家自动退役;
- 管理 API:监听 8791 端口,提供手动触发同步(
POST /api/v1/sync/jobs)等接口,供管理后台"一键同步"。
它还内置了一套资源护栏:磁盘暂存区到 40 GB 停止扩容、可用内存低于 2 GiB 拒绝启动、临时产物定期清理——把大数据 ETL 的稳定性焦虑消化在进程内部。
六、六个服务如何启动:依赖顺序是灵魂
docker-compose.yml里的启动顺序编排值得细品:
bootstrap(生成密钥) └─► postgres(健康检查通过) └─► migrate(版本化迁移,跑完退出) └─► credential-broker(密钥代理就绪) ├─► api(对外服务) └─► sync(后台工厂)migrate一次性迁移用restart: on-failure:3兜底,跑完即成功退出;api和sync都声明了/health或/api/v1/health健康检查,Compose 会持续探活;- 每个服务还挂了
json-file日志轮转(20 MB × 5 份),防止磁盘被日志吃光。
七、架构小结:这套设计好在哪?
- 一个镜像 = 一份代码真相:前端、API、ETL 全部同源构建,不会出现"前端版本和 API 版本对不上"的事故;
- 静态化前端:Astro 纯静态输出让 Hono 单进程就能服务页面,省掉一层 Web 服务器;
- 数据原子性优先:候选区 + 事务发布保证"坏数据永远到不了线上";
- 安全默认值:内网隔离、低权限运行、密钥文件化、API 限流,全部内建而非靠运维自觉。
如果你正在寻找一个能自托管、基于真实开放数据的地址生成器,这套"单镜像多服务"的架构本身就是一份不错的参考教材。
延伸阅读
- 开发指南(架构与目录说明):docs/DEVELOPMENT.zh-CN.md
- 部署文档:docs/DEPLOYMENT.zh-CN.md
- API 文档(中/英/繁三语):docs/API.zh-CN.md
- 同步服务说明:server/sync/README.md
- 各国地址生成策略:docs/strategies/
【免费下载链接】addressA self-hosted address and synthetic test-profile generator for 27 countries and regions, built from real open-data streets, administrative areas, coordinates, and postcodes. Supports multilingual output, IP-nearby generation, map previews, and API access. 基于真实开放数据的自托管地址与合成测试资料生成器,覆盖 27 个国家和地区,支持多语言地址、IP 附近生成、地图预览与 API 调用项目地址: https://gitcode.com/gh_mirrors/address4/address
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考