不是。单机启动多个go服务只是多进程,不等于分布式;真正的分布式需模拟网络分区、服务发现、节点动态上下线与故障传播,必须通过docker compose等容器化方式隔离进程边界、使用注册中心实现动态服务发现,并禁用dns缓存以真实复现生产环境行为。

单机跑多个 Go 服务是否等于分布式?
不是。本地启动 go run main.go 启动 5 个不同端口的 HTTP 服务,只是“多进程”,不是“分布式”。真正的分布式环境需要模拟网络分区、服务发现、节点动态上下线、故障传播等行为。单机模拟的目标是让每个服务像真实集群节点一样:有独立生命周期、能被其他节点发现、可单独重启/崩溃、不共享内存或文件系统。
用 Docker Compose 搭建可观察的多节点拓扑
别手写 sh -c 'go run s1.go & go run s2.go & wait'——信号丢失、日志混杂、无法 scale、健康检查失效。必须用容器隔离进程边界:
- 每个 Go 服务打包为独立镜像(哪怕只是
FROM golang:1.22-alpine+COPY . .+go build -o /app/server) - 在
docker-compose.yml中定义至少 3 类服务:auth(gRPC)、api(HTTP)、registry(etcd 或 Consul) - 全部接入自定义 bridge 网络:
networks: [mynet],禁用host或default网络 - Go 代码里所有外部依赖地址必须通过环境变量注入,例如
os.Getenv("REGISTRY_ADDR"),而非硬编码"localhost:2379"
这样启动后,api 容器内执行 curl http://auth:8081/health 能通,且 docker stop auth 后 api 的重试逻辑会真实触发——这才是可验证的“节点失效”场景。
服务发现必须走中间件,不能靠 DNS 或 hosts
别在 /etc/hosts 里写死 172.20.0.3 auth-service,也别依赖 Docker 内置 DNS 的静态解析。真实分布式环境里,节点 IP 是动态分配的,服务名到 IP 的映射必须由注册中心维护:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 选轻量级注册中心:
consul agent -dev -client=0.0.0.0 -bind=0.0.0.0(开发用),或etcd --advertise-client-urls=http://0.0.0.0:2379 - 每个 Go 服务启动时调用 SDK 注册:
consul.AgentServiceRegister(...),带 TTL 心跳 - 调用方不直连 IP,而是先查注册中心:
consul.Health().ServiceNodes("auth", nil),再轮询可用节点 - 务必关闭 Go 的 DNS 缓存:
import _ "net/http/httptrace"不够,要显式设置net.DefaultResolver.PreferGo = true并禁用系统 resolv.conf 缓存
否则你会遇到“服务重启后旧 IP 还在客户端缓存里,请求持续失败 30 秒”的问题——这在单机模拟时反而更容易暴露。
本地调试时如何避免端口冲突和资源争抢?
开发阶段频繁 docker-compose up -d 再 down,容易残留网络、卷、IP 分配。关键控制点:
- 为每个服务指定固定端口映射仅用于**对外调试**:
ports: ["8080:8080"],但服务间通信永远走内部 DNS 名 + 容器端口(如http://cache:6379) - Go 程序监听必须是
0.0.0.0:$PORT,不是127.0.0.1:$PORT——后者在容器内根本收不到其他容器发来的包 - 避免多个服务写同一本地路径:
./logs或./data,改用/tmp/$SERVICE_NAME或挂载 volume 到唯一子目录 - 如果用 SQLite 做临时存储,每个服务必须用不同 db 文件路径,且加
mode=wal参数防止并发锁死
最易被忽略的是 Go 的 http.Server 默认超时配置——本地模拟时没负载,但生产环境网路抖动会导致连接堆积。务必显式设 ReadTimeout、WriteTimeout 和 IdleTimeout,否则一个卡住的连接会拖垮整个服务实例。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










