Skip to content

服务与拓扑

IoT DC3 不是一个单体进程,而是一组可独立部署、靠 gRPC 与 RabbitMQ 协作的服务。这页讲清平台由哪些可部署单元构成、它们之间怎么连线、以及为什么必须按某个固定顺序启动——读完你能看懂 docker-compose.yml 里的每一条 depends_on,也能自己排查"为什么网关起不来"。

你在这里:已读 系统架构总览,想把"网关 + 四个中心 + 驱动" 落到具体进程、端口和启动次序。下一步可看 Facade 模式 理解服务间怎么调用,或直接 快速开始 把这套栈跑起来。

为什么拆成这么多单元

把平台拆成网关、四个中心和一组驱动,不是为了"微服务而微服务" ,而是因为这几类职责的伸缩与故障边界天然不同:南向的协议驱动数量多、按现场扩,和北向的元数据管理完全两回事;鉴权是所有请求的必经关卡,应当独立且先就绪;时序数据写入吞吐高,需要单独的数据中心承接 RabbitMQ 消息洪峰。拆开之后,每一类单元可以单独扩容、单独重启、单独定位问题。

平台共有六类可部署单元,外加一个把全部中心打包进单进程的 single 单体:

  • 网关 Gateway(dc3-gateway——唯一的对外 API 聚合入口,做鉴权头解析、HMAC 签名注入、路由转发,并承载 MCP 资源服务器 /mcp(app 栈下经 Web 前端反代触达,见下文端口小节)。
  • 鉴权中心 Auth Center(dc3-center-auth——认证、租户、RBAC、OAuth 2.1 授权服务器。无业务依赖,最先就绪。
  • 管理中心 Manager Center(dc3-center-manager——驱动、模板、设备、位号等元数据管理。
  • 数据中心 Data Center(dc3-center-data——位号值落库、命令分发与回执、告警引擎。
  • 智能中心 Agentic Center(dc3-center-agentic——Spring AI 会话、工具调用、对话持久化。
  • 协议驱动 Drivers(dc3-driver-*——驱动目录共 36 个协议适配实现,docker-compose.yml 默认内置全部 36 个驱动容器;南向接设备、北向经 RabbitMQ 与数据中心解耦。
  • single 单体(dc3-center-single——把四个中心的能力合并进一个进程,用 dc3.facade.mode: local 在进程内直连,适合本地开发与轻量部署(见 Facade 模式)。

中心服务首次出现给"中文名 + 标识"

后文沿用术语表口径,"数据中心"即 dc3-center-data,"网关"即 dc3-gateway,不再重复全称。

谁监听哪个端口

每个单元同时可能暴露一个 HTTP 端口(对外或对内的 REST 入口)和一个 gRPC 端口(中心之间 facade 调用)。关键约束:网关是唯一的对外 API 聚合入口,但它的 HTTP 8000 在 app 栈里并不发布到宿主机——docker-compose.yml 只把 Web 前端 8080/8443 与 listening-virtual 驱动的设备入站口映射到宿主机,外部请求经 Web 前端的 nginx 反代到容器内的 dc3-gateway:8000。网关 8000 与其余中心的 HTTP 端口一样只在容器网络内可达;只有在 dev 栈(docker-compose-dev.yml)里网关 8000 才发布到宿主机(同时各中心端口也一并发布)。生产环境不应把后端端口映射到宿主机。

下图按"谁依赖谁先就绪"画出服务拓扑:实线箭头是 depends_on 健康依赖,标注的是各服务的 HTTP / gRPC 端口。

db 栈 docker-compose-db.yml · 先起并等健康应用栈 docker-compose.yml · depends_on: condition: service_healthynginx 反代gRPC 核验位号值发布 (MQ)消费 point_valueJDBCdepends_on 健康依赖depends_ondepends_ondepends_on · gRPC 注册depends_on部署拓扑二选一PostgreSQL + TimescaleDB:5432 · pg_isready 健康检查pgvector · AGERabbitMQ 消息总线:5672 · rabbitmq-diagnostics ping管理台 :15672 · MQTT :1883鉴权中心 dc3-center-authHTTP 8300 · gRPC 9300无业务依赖 · 最先就绪readiness /auth/actuator/… dc3-gateway:8000 · 唯一 API 聚合入口app 栈不发布到宿主机 · 承载 /mcp管理中心 dc3-center-managerHTTP 8400 · gRPC 9400驱动 / 模板 / 设备 / 位号元数据数据中心 dc3-center-dataHTTP 8500 · gRPC 9500位号值落库 · 命令分发 · 告警智能中心 dc3-center-agenticHTTP 8600Spring AI 会话 · 工具调用协议驱动 dc3-driver-*36 个协议适配容器南向接设备 · 北向经 MQWeb 前端 dc3-web8080 / 8443 · 对宿主机唯一发布的 HTTPnginx 反代 → dc3-gateway:8000dc3-center-singleHTTP 8100 · gRPC 9100四中心合一进程dc3.facade.mode: local本地开发 / 轻量部署app 栈对宿主机仅发布: Web 前端 8080/8443 与 listening-virtual 设备入站口 6270/6271; 网关 8000 仅 dev 栈发布中心服务 readiness 探针带 base-path 前缀 (如 /auth/actuator/health/readiness), 网关不带; 驱动可与网关并行启动入口 / 单体中心服务 / 驱动PostgreSQLRabbitMQ鉴权中心健康依赖运行时连接

端口分配是有规律的:HTTP 端口 83/84/85/86xx 与 gRPC 端口 93/94/95xx 一一对应到 auth/manager/data。智能中心目前只暴露 HTTP 8600。single 单体则独占 HTTP 8100 / gRPC 9100DC3_SINGLE_PORT / DC3_SINGLE_GRPC_PORT),与分布式栈端口不冲突,可在同机并存。

下表把上图的端口与对宿主机的发布情况固化为参考;写代码或配 Nginx 反向代理时以此为准。"对宿主机发布"一列指 app 栈( docker-compose.yml)的实际 ports: 映射:

单元HTTPgRPCapp 栈对宿主机发布环境变量(发布端口)
Web 前端 dc3-web8080 / 8443是(app 栈唯一 HTTP 入口,nginx 反代到网关)DC3_WEB_HTTP_PORT / DC3_WEB_HTTPS_PORT
网关 dc3-gateway8000否(仅容器网络内可达;dev 栈才用 DC3_GATEWAY_PORT 发布)DC3_GATEWAY_PORT
鉴权中心 dc3-center-auth83009300DC3_AUTH_PORT / DC3_AUTH_GRPC_PORT
管理中心 dc3-center-manager84009400DC3_MANAGER_PORT / DC3_MANAGER_GRPC_PORT
数据中心 dc3-center-data85009500DC3_DATA_PORT / DC3_DATA_GRPC_PORT
智能中心 dc3-center-agentic8600DC3_AGENTIC_PORT
single 单体 dc3-center-single81009100视部署DC3_SINGLE_PORT / DC3_SINGLE_GRPC_PORT

后端 HTTP 端口在 app 栈里都不发布到宿主机

docker-compose.yml(app 栈)里,网关与 auth/manager/data/agentic 都没有 ports: 映射——它们只在 dc3net 容器网络内可达。被映射到宿主机的只有 Web 前端 8080/8443DC3_WEB_HTTP_PORT / DC3_WEB_HTTPS_PORT)和 listening-virtual 驱动的设备入站口 TCP 6270 / UDP 6271DC3_LISTENING_VIRTUAL_TCP_PORT / ..._UDP_PORT)。因此在 app 栈下,从外部访问业务 API 要走 Web 前端 8080,由其 nginx 反代到容器内的 dc3-gateway:8000;宿主机无法直连 8000。只有 dev 栈( docker-compose-dev.yml)才把网关 8000(及各中心端口)发布到宿主机,可直连。

以什么顺序启动

服务之间有硬性的就绪顺序:网关要给请求注入鉴权信息,就得 auth 先在;data 落库和分发命令要先有 manager 的元数据;agentic 要读数据、调命令,就得 auth/manager/data 都在。这套顺序不是写在文档里靠人记,而是用 Compose 的 depends_on: condition: service_healthy 强制——被依赖方健康检查通过,依赖方才启动

健康判定统一用各服务的 /actuator/health/readiness(readiness 探针)。注意中心服务的 readiness 路径带 base-path 前缀,网关不带:

  • 网关:http://127.0.0.1:8000/actuator/health/readiness
  • 鉴权中心:http://127.0.0.1:8300/auth/actuator/health/readiness
  • 管理中心:http://127.0.0.1:8400/manager/actuator/health/readiness
  • 数据中心:http://127.0.0.1:8500/data/actuator/health/readiness
  • 智能中心:http://127.0.0.1:8600/agentic/actuator/health/readiness

下图按依赖链画出从基础依赖到驱动的整条就绪时序——每一跳都等上游 readiness 通过后才开始。

pg_isready · rabbitmq pingdb 栈健康① db 栈健康 → 启动应用栈readiness /auth/actuator/…→ UP② healthy②' healthy (与网关并行)readiness 无 base-path 前缀→ UPreadiness 带 base-path 前缀→ UP③ healthyreadiness /data/actuator/…→ UP④ healthy④' healthy (仅依赖 manager)readiness → UP开始服务请求⑤ gRPC 注册 (协议属性定义)⑥ 位号值发布 point_value启动顺序由 depends_on: condition: service_healthy 强制,不靠人工 sleep;驱动只依赖 manager,与管理中心就绪后即可启动、无需等网关;网关与管理中心在 auth 就绪后并行启动db 栈PostgreSQL + RabbitMQdc3-center-auth:8300/:9300 · 最先就绪dc3-gateway:8000dc3-center-manager:8400/:9400dc3-center-data:8500/:9500dc3-center-agentic:8600dc3-driver-*36 个协议驱动基础设施鉴权中心中心服务协议驱动启动许可注册 / 数据

这张图也解释了一个常见现象:驱动只依赖 manager,不依赖网关或数据中心。驱动起来后向管理中心 gRPC 注册自己(带协议属性定义),再开始按计划采集、经 RabbitMQ 把位号值推给数据中心——所以驱动可以和网关并行启动,无需等网关就绪。

基础依赖必须先于全部应用就绪

docker-compose.yml(应用栈)里的中心服务不包含 PostgreSQL 与 RabbitMQ——它们在独立的 docker-compose-db.yml(db 栈)里,分别以 pg_isreadyrabbitmq-diagnostics ping 做健康检查。应用栈假定这两者已经健康。因此正确的启动序列是先起 db 栈、等其健康,再起应用栈;跳过这一步,auth 会因连不上库反复重启。对应命令见下。

把这套栈跑起来

实际操作分两步:先拉起 db 栈(PostgreSQL + RabbitMQ),再拉起应用栈。Makefile 把 Compose 细节包好了,命令在 iot-dc3/ 目录下执行:

bash
# 1. 先起基础依赖,等健康
make up-db

# 2. 起应用栈(构建镜像 + 按 depends_on 顺序启动)
make up STACK=app

# 跟随日志,确认各服务 readiness 依次通过
make logs STACK=app
bash
# db 栈:postgres + rabbitmq
podman compose -f dc3/docker-compose-db.yml up -d

# 应用栈:gateway + 四中心 + 驱动
podman compose -f dc3/docker-compose.yml up -d

# 校验 compose 语法
podman compose -f dc3/docker-compose.yml config --quiet

起栈后确认整链就绪。注意 app 栈下网关 8000 不发布到宿主机,宿主机直连 127.0.0.1:8000 会连接失败——要么进网关容器内探(与容器 healthcheck 同址),要么从宿主机走 Web 前端 8080

bash
# app 栈:在网关容器内探 readiness(期望返回 {"status":"UP"})
podman exec dc3-gateway curl -fsS http://127.0.0.1:8000/actuator/health/readiness

# 宿主机入口是 Web 前端 8080(nginx 反代到 dc3-gateway:8000)
curl -fsS http://127.0.0.1:8080/

dev 栈才能从宿主机直连网关 8000

若用 make up-dev(dev 栈,docker-compose-dev.yml)启动,网关 8000 会发布到宿主机,此时可直接 curl -fsS http://127.0.0.1:8000/actuator/health/readiness

本地开发可用 single 单体免去多进程编排

若只想在本机快速验证业务逻辑,不必拉起六个容器:dc3-center-singledc3.facade.mode: local 在进程内直连各中心能力,监听 HTTP 8100 / gRPC 9100。分布式与单体之间只是部署拓扑差异,不改变业务语义——详见 Facade 模式

约束与边界

  • 网关是唯一的对外 API 聚合入口,但宿主机入口因栈而异。在 app 栈(docker-compose.yml)里只有 Web 前端 8080/8443 与 listening-virtual 的设备入站口 TCP 6270/UDP 6271 映射到宿主机,网关 8000 不发布、外部请求经 Web 前端 nginx 反代到 dc3-gateway:8000;dev 栈(docker-compose-dev.yml)才把网关 8000 与各中心端口一并发布到宿主机。无论哪种栈,其余后端端口都只在容器网络内可达,生产环境不要映射到宿主机。
  • 启动顺序由健康检查保证,不靠人工 sleepdepends_on: condition: service_healthy 让依赖方等到被依赖方 readiness 通过才启动;但这只覆盖应用栈内部,db 栈必须由你先行拉起。
  • readiness 路径带 base-path。中心服务用 webflux.base-path(如 auth 的 /auth),探针路径相应带前缀;网关不带。写监控/探活脚本时别漏掉前缀。
  • 分布式默认走 gRPC facade。manager 等中心的 dc3.facade.mode 默认 ${DC3_FACADE_MODE:grpc}dc3/env/dev.env 也设为 grpc;single 单体的基础 application.yml 才声明 local 。这是部署拓扑选择,不是协议选择,细节见 Facade 模式

延伸阅读

基于 AGPL-3.0 协议发布