PHP微服务拆分必须先做领域隔离,即在单体中用命名空间和静态分析工具划清边界、禁用跨域调用;Service Mesh接入应通过Sidecar劫持流量,而非重写PHP客户端代码。

PHP 微服务拆分必须先做领域隔离,而不是直接切代码
拆分失败的 PHP 项目,90% 都卡在“物理拆了,逻辑还缠着”。比如把 UserController 搬进新仓库,但里面仍调用原单体的 OrderService::calculateDiscount(),这种只是目录搬家,不是微服务。
真正有效的起点是:在现有单体中先用命名空间和包结构划清边界,禁用跨领域引用。例如用户域只允许访问 App\Domain\User\* 下类,订单域只允许 App\Domain\Order\*,用 PHPStan 或 Psalm 配置规则强制拦截越界调用。
常见错误现象:Class 'App\Services\PaymentGateway' not found 在新服务里报错,本质是没识别出该类实际属于支付域,不该被用户服务直接依赖。
- 先梳理核心业务流(如注册→下单→支付),标注每步的数据表、实体、外部依赖
- 每个服务只保留自己领域的
Model、Repository、DomainEvent和对外契约Contracts/ - 禁止在
app/下写跨域逻辑;所有服务间交互必须走 API 或消息,不走 PHP 类调用
PHP 服务接入 Service Mesh 的关键动作是注入 Sidecar,不是改 Guzzle
很多团队花两周重写所有 GuzzleHttp\Client 调用,加熔断、重试、超时——这是反模式。Service Mesh 的价值恰恰在于让这些治理逻辑从 PHP 代码里消失。
立即学习“PHP免费学习笔记(深入)”;
正确路径是:保持原有 HTTP 调用不变(比如仍用 $client->get('http://user-service/api/users/1')),通过 Istio 或 CSE Mesher 在 Pod 级别注入 Envoy Sidecar,由它劫持出站流量并自动完成服务发现、负载均衡、mTLS 加密。
性能影响很小:Envoy 基于 C++ 实现,实测 PHP 进程与 Sidecar 间 Unix Domain Socket 通信延迟 proxy_redirect 或开启冗余日志,会拖慢首字节响应时间。
- PHP 容器镜像无需重新打包,只要确保监听地址为
127.0.0.1:8080(非0.0.0.0),避免流量绕过 Sidecar - 在 Kubernetes 中为 PHP Deployment 添加
sidecar.istio.io/inject: "true"注解 - 验证是否生效:进入 Pod 执行
curl -v http://user-service/api/users/1,看响应头是否有x-envoy-upstream-service-time
PHP 微服务不能共享数据库,但可以共享迁移工具链
“独立数据存储”不是指每个服务配一台 MySQL 实例(成本太高),而是指每个服务拥有自己私有的 Schema 和读写权限。常见错误是多个服务共用一个 users 表,靠应用层约定“谁改哪几列”,这迟早引发数据竞争或字段语义漂移。
可行方案:用同一套 doctrine/migrations 工具管理各服务的独立 migration 文件,但每个服务只执行自己目录下的 src/Migrations/UserService/ 或 src/Migrations/OrderService/,数据库账号按 Schema 严格隔离。
兼容性注意点:Laravel 的 php artisan migrate 默认跑全量 migration,必须配合 --path 参数限定范围,否则订单服务上线会误删用户服务的表。
- 禁止跨 Schema JOIN 查询;关联数据通过 API 调用或 CQRS 同步到本地只读副本
- 敏感操作如密码重置,必须由用户服务提供幂等接口
POST /api/password-reset-requests,订单服务不得直连password_resets表 - 使用
mysqldump --no-create-info --skip-triggers导出生产数据做测试时,只导对应 Schema
PHP 微服务的健康检查端点必须暴露真实状态,不能只返回 200
Nginx 或 Istio 的存活探针如果只调 GET /health 并检查 HTTP 状态码,会掩盖真实问题。比如 PHP-FPM 进程卡死、Redis 连接池耗尽、MySQL 主从延迟 > 30s,但 /health 仍返回 200。
应该让健康检查端点主动探测关键依赖:检查 Redis PING 响应时间 SELECT 1 耗时 user-service 可连通且返回 200。任一失败就返回 503 并带具体原因。
容易踩的坑:在 Laravel 中用 Artisan::call('down') 触发维护模式,但未同步更新 /health 响应,导致 K8s 认为服务正常而持续转发流量。
- 不要复用
/或/ping作为健康检查路径;明确用/health/live(进程存活)和/health/ready(依赖就绪) - Istio 的
readinessProbe必须配置initialDelaySeconds: 15,给 PHP-FPM 和 Composer autoload 缓存加载留出时间 - 在 Swoole 场景下,
/health/ready还需校验 Worker 进程数是否达到预期,避免 reload 后部分进程未启动



















