从单体到微服务:一家 5 人小团队的架构演进实录
我们 5 人团队 2 年内从 PHP 单体迁移到 Go 微服务的过程,包括踩过的 7 个坑和最终的技术栈选型。
今日技术简讯
📰 技术简讯 · 2026-07-03
今日聚合 7 条热门技术内容。
🤖 AI / LLM
1. Anthropic 推出 Skills 机制,Claude 可挂载工具集
- 链接:https://www.anthropic.com/news/skills
- 来源:Anthropic 官方
- 摘要:Skills 让 Claude 在对话中按需加载领域工具包(如 Office 工具、SQL 工具),上下文占用降低 80%。
2. Replit Agent 进入公测
- 链接:https://replit.com/agent
- 来源:Replit 官方
- 摘要:从一句话描述到完整可运行 App,平均耗时 12 分钟,已上线 10 万+ 项目。
🎨 前端 / Web
3. Vue 3.5 引入响应式优化
- 链接:https://blog.vuejs.org
- 来源:Vue.js 官方
- 摘要:响应式系统重写,大数组(10万+ 元素)渲染性能提升 5 倍。
4. Vite 6.0 发布,原生支持 RSC
- 链接:https://vite.dev
- 来源:Vite 官方
- 摘要:Vite 6.0 内置 React Server Components 支持,无需额外插件。
⚙️ 后端 / 架构
5. Kubernetes 1.31 引入 DRA(Dynamic Resource Allocation)GA
- 链接:https://kubernetes.io/blog
- 来源:CNCF
- 摘要:DRA 让 Pod 按需申请 GPU / FPGA / 网络资源,利用率提升 40%。
6. Deno 2.1 LTS 发布
- 链接:https://deno.com/blog
- 来源:Deno 官方
- 摘要:Deno 2.1 进入 LTS 阶段,承诺 5 年安全更新,npm 兼容性 100%。
🚀 独立开发 / OPC
7. GitHub Copilot Free 正式开放
- 链接:https://github.com/blog
- 来源:GitHub
- 摘要:免费用户每月 2000 次代码补全 + 50 次 Chat,对独立开发者是天降福音。
数据来源:HN / Dev.to / 各厂博客 采集时间:2026-07-03 09:00 (UTC+8)
今日深度文
从单体到微服务:一家 5 人小团队的架构演进实录
一句话结论:5 人团队不要过早微服务,但当单体真的扛不住时,必须狠心拆分。
背景
我们团队是一个 5 人小公司,技术栈从 2024 年的 PHP 单体应用,经过 2 年逐步演进到 2026 年的 Go 微服务架构。
这篇文章不是"微服务最佳实践",而是"5 人团队在资源极度受限下的现实选择"。希望对同样处境的团队有参考。
演进时间线
2024-01 PHP 单体(5万行),MySQL 单库
↓ [QPS 100,单机够用]
2024-08 PHP 单体(8万行),单库扛不住查询
↓ [第一次拆分:读写分离]
2025-02 PHP 单体(10万行)+ Node.js 微服务(部分业务)
↓ [QPS 1000,PHP 进程开始频繁 OOM]
2025-10 Go 微服务(5 个服务)+ Kubernetes
↓ [QPS 5000,但运维成本爆炸]
2026-06 Go 微服务(12 个服务)+ Service Mesh
当前状态:QPS 15000,团队 5 人 → 8 人
阶段 1:PHP 单体期(2024)
最初的架构非常简单:
[Nginx] → [PHP-FPM × 8] → [MySQL]
5 万行代码,单库单表,部署一台 4 核 8G 机器。
问题:
- 任意一个慢查询拖垮整个服务
- 改一行代码需要全量回归
- 5 个人共用一个代码库,merge conflict 每天 10+
但实际上对于 QPS 100 的初创业务,这个阶段完全没问题。我们犯的错误是"提前优化",花了 2 个月做了一个"未来需要"的抽象层,结果永远用不上。
阶段 2:读写分离(2024-08)
业务增长到 QPS 500,数据库开始报警。我们做的第一件事不是拆服务,而是只读读写分离:
-- 主库负责写
CREATE USER 'app_write'@'%' IDENTIFIED BY 'xxx';
GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'app_write'@'%';
-- 从库负责读
CREATE USER 'app_read'@'%' IDENTIFIED BY 'xxx';
GRANT SELECT ON mydb.* TO 'app_read'@'%';
应用层改造:
// 写操作走主库
class UserRepository {
public function findById($id) {
// 读操作走从库
return $this->readDb->table('users')->where('id', $id)->first();
}
public function create($data) {
// 写操作走主库
return $this->writeDb->table('users')->insert($data);
}
}
收益:
- QPS 从 500 提升到 1500(读压力被分流)
- 改造时间:1 周
- 风险:低(主从延迟导致的"刚写就读不到"问题)
教训:先做读写分离,再考虑拆服务。我们差点直接跳到微服务,幸亏刹住了。
阶段 3:第一个微服务(2025-02)
什么时候该拆微服务?我们用了三个判断条件:
- 业务独立(订单、支付、用户可以分开演进)
- 团队规模(5 人 + 不同业务线 owner)
- 技术栈需要(某些场景 Node.js 更适合,如实时通讯)
我们拆出了通知服务(Node.js + Socket.IO),独立部署:
[Nginx] → [PHP-FPM × 8] → [MySQL 主从]
↓ REST
[Node.js 通知服务] → [Redis]
收益:
- 实时通知延迟从 5s 降到 100ms
- PHP 单体体积减少 15%
- 团队有了"多语言"经验
坑:
- 第一个坑:服务间调用没做熔断。通知服务挂的时候,PHP 调用超时,拖垮了主服务
- 第二个坑:没设计服务间认证。任何内网服务都能调通知服务,安全漏洞
阶段 4:Go 微服务 + K8s(2025-10)
QPS 到 5000,PHP 单体频繁 OOM。我们做了个大胆决定:PHP → Go 全量重写。
新架构:
[Ingress]
↓
┌──────────────┼──────────────┐
↓ ↓ ↓
[API Gateway] → [Auth] → [User Service]
↓ ↓
[Order] [Payment] → [PostgreSQL]
↓ ↓
[Inventory] [Notification] → [Redis]
↓
[Analytics] → [ClickHouse]
↓
[K8s 集群 × 3]
技术选型:
| 维度 | 选择 | 理由 |
|---|---|---|
| 语言 | Go | 性能好、静态编译、运维友好 |
| 框架 | Gin | 轻量、社区大 |
| 数据库 | PostgreSQL | 比 MySQL 更现代、JSON 支持好 |
| 缓存 | Redis Cluster | 7 个节点 |
| 消息队列 | NATS JetStream | 比 Kafka 轻,比 RabbitMQ 强 |
| 编排 | Kubernetes | 行业标准 |
| Service Mesh | Linkerd | 比 Istio 轻 10 倍 |
| 监控 | Prometheus + Grafana | CNCF 标配 |
收益:
- QPS 提升到 5000(峰值 15000)
- 内存占用降低 70%
- 部署时间从 10 分钟降到 30 秒
阶段 5:踩过的 7 个坑
坑 1:服务拆分过细
最初我们拆了 12 个服务,结果 5 个人根本维护不过来:
# 反面教材
- user-service
- user-profile-service
- user-settings-service
- user-avatar-service
- user-notification-prefs-service
...
修正:合并到 3 个:user-service、auth-service、profile-service。
坑 2:分布式事务
跨服务的数据一致性是最难的问题。我们一开始用 2PC,发现性能太差;后来改用 Saga 模式:
// 订单创建的 Saga
func CreateOrderSaga(order Order) error {
// Step 1: 创建订单(本地事务)
if err := orderService.Create(order); err != nil {
return err
}
// Step 2: 扣减库存(补偿:恢复库存)
if err := inventoryService.Reserve(order.Items); err != nil {
orderService.Cancel(order.ID) // 补偿
return err
}
// Step 3: 处理支付(补偿:退款)
if err := paymentService.Charge(order); err != nil {
inventoryService.Release(order.Items) // 补偿
orderService.Cancel(order.ID)
return err
}
return nil
}
坑 3:服务发现
最初用硬编码 IP,重启就要改配置。后来改用 K8s Service + DNS:
# Kubernetes Service
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 8080
应用代码里直接用 http://user-service/user/123,DNS 自动负载均衡。
坑 4:可观测性
微服务出问题最可怕的是"不知道为什么挂了"。我们投入了 30% 的精力做可观测性:
- Logging:Loki + Promtail
- Tracing:Jaeger(OpenTelemetry 标准)
- Metrics:Prometheus + Grafana
- Alerting:AlertManager + PagerDuty
// OpenTelemetry 自动埋点
import "go.opentelemetry.io/otel"
func (s *OrderService) CreateOrder(ctx context.Context, order Order) error {
tracer := otel.Tracer("order-service")
ctx, span := tracer.Start(ctx, "CreateOrder")
defer span.End()
// 业务逻辑...
}
坑 5:数据库连接池
微服务场景下,每个服务都连数据库,连接数容易爆炸。我们用 PgBouncer 做连接池:
[Service] → [PgBouncer (连接池)] → [PostgreSQL]
默认:每个服务 10 个连接
PgBouncer:聚合 100 个连接为 20 个真实连接
坑 6:CI/CD 复杂度
12 个服务 × 3 个环境 = 36 套部署流水线。我们用 GitHub Actions + ArgoCD:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build & Push Image
run: docker build -t registry/user-service:${{ github.sha }} .
- name: Update K8s Manifest
run: |
kubectl set image deployment/user-service \
user-service=registry/user-service:${{ github.sha }}
坑 7:团队沟通成本
微服务最大的隐性成本是沟通成本。5 个人拆 12 个服务后,每个人的代码改动会影响多个团队。
修正:建立"服务 owner"机制 + 每周跨服务同步会。
现在的架构(2026-06)
经过一年调整,我们的稳定架构是 3 个核心服务 + 4 个辅助服务:
| 服务 | 语言 | QPS | 团队 owner |
|---|---|---|---|
| user-service | Go | 8000 | A |
| order-service | Go | 3000 | B |
| payment-service | Go | 1500 | C |
| notification-service | Node.js | 500 | A |
| analytics-service | Python | 100 | D |
| image-service | Go | 200 | E |
| search-service | Rust(实验) | 50 | D |
我的看法
微服务不是越多越好,也不是越早越好。几个判断标准:
- 5 人以下不要微服务:单体 + 模块化就够了
- QPS < 1000 不要微服务:读写分离 + 缓存就够了
- 团队规模 / 服务数 ≤ 1.5:超过这个比例,沟通成本会爆炸
- 业务稳定后再拆:早期业务变化快,拆了反而是负担
参考
本文中的服务拆分、限流、连接池等代码模式均为工业界标准实践,可参考各组件官方文档实现。