ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【基于 Swoole+Hyperf 的微服务实战】 第四周·周五:将文章系统和用户服务全量接入 Consul + Nacos,验证无感扩容

【基于 Swoole+Hyperf 的微服务实战】 第四周·周五:将文章系统和用户服务全量接入 Consul + Nacos,验证无感扩容 今天是第二阶段“微服务通信与治理”的收官之战。今天我们将完成一个关键实战将文章系统和用户服务全量接入 Consul Nacos验证无感扩容。前面四天我们分别完成了服务注册、服务发现、配置中心集成和多环境配置管理今天要把这些能力串联起来模拟生产环境中服务实例动态伸缩的场景让消费者在不重启的情况下自动感知变化实现真正的弹性伸缩。今日目标确保文章服务HTTP和用户服务JSON-RPC均已接入 Consul 和 Nacos。以用户服务为例通过启动多个实例来模拟扩容通过停止实例模拟缩容。观察文章服务通过 Consul 服务发现动态获取最新的用户服务节点列表负载均衡器自动将流量分配到新旧实例。结合 Nacos 动态配置实时调整限流阈值验证配置变更无需重启。使用压测工具观察整个过程中的服务连续性和错误率确保“无感”。一、环境准备与现状确认约 30 分钟继续在hyperf-app项目中确保 Docker 已启动并进入容器docker-composeexecswoolebashcd/var/www/hyperf-app1. 确认当前服务架构HTTP 服务文章/订单监听0.0.0.0:9501用户服务UserService监听0.0.0.0:9502我们之前只保留了一个节点用于演示商品服务ProductService监听0.0.0.0:9504Consul 中应该已经注册了这两个服务可以通过 UIhttp://localhost:8500查看。Nacos 中也已经有了hyperf-app配置dev 命名空间包含限流参数等。2. 启停问题由于我们只有一个 Docker PHP 容器要启动多个用户服务实例我们有两个选择在同一个 Hyperf 进程中添加另一个 JSON-RPC 服务器监听不同端口如9503并确保它也被注册到 Consul需要另一个RpcService实现或手动注册。使用 Docker 再启动一个相同的 PHP 容器或用不同端口启动另一个 Hyperf 进程但需要共享代码。考虑到开发环境简单性我们采用在同一个 Hyperf 应用中启用第二个 JSON-RPC 用户服务节点并手动将其注册到 Consul。以前我们的server.php中曾配置过jsonrpc2端口 9503今天恢复它并让用户服务也绑定到该服务器以便注册两个实例。二、知识核心回顾约 20 分钟服务注册Provider 启动时自动向 Consul 注册健康检查维持心跳。服务发现Consumer 通过 Consul 定期拉取健康的节点列表配合负载均衡器使用。动态扩容新实例注册 → Consul 通知或消费者刷新列表 → 流量自然导入新实例。动态缩容实例下线或健康检查失败 → Consul 标记不健康 → 消费者刷新后剔除该节点。配置动态下发Nacos 配置变更 → 应用热加载无需重启。今天要验证的核心就是扩容和缩容的过程对调用方完全透明消费者自动适应。三、实战模拟动态伸缩全过程约 3 小时步骤 1恢复多节点用户服务编辑config/autoload/server.php在servers数组中加入第二个 JSON-RPC 服务器如果之前注释掉了[namejsonrpc2,typeServer::SERVER_HTTP,host0.0.0.0,port9503,sock_typeSWOOLE_SOCK_TCP,callbacks[Event::ON_REQUEST[\Hyperf\JsonRpc\HttpServer::class,onRequest],],],为了让第二个端口也服务于UserService我们需要一个额外的RpcService实现类。简单地我们可以复制UserService并修改注解中的server为jsonrpc2。创建app/JsonRpc/UserService2.php?phpnamespaceApp\JsonRpc;useHyperf\RpcServer\Annotation\RpcService;#[RpcService(name:UserService,protocol:jsonrpc-http,server:jsonrpc2)]classUserService2extendsUserService{// 直接继承行为一致}同样在dependencies.php中可能需要绑定但 Hyperf 会自动扫描注解并注册为服务因此只要我们配置了jsonrpc2服务端这个类就会被注册为一个 RPC 服务提供者端口 9503 也会加入UserService的节点列表。注意此时UserService和UserService2都实现了UserServiceInterfaceUserService2继承了UserService而UserService已经实现了UserServiceInterface所以没问题。步骤 2确认 Consul 注册配置在config/autoload/services.php的drivers.consul中确保check配置存在昨天已配。同时确保consumers.UserService使用registry指向 Consulrefresh_time设为较小的值如 5 秒以便快速观察。步骤 3启动多实例用户服务重启 Hyperf 应用php bin/hyperf.php start观察控制台输出应显示jsonrpc(9502) 和jsonrpc2(9503) 均启动。查看 Consul UI (http://localhost:8500)UserService下应出现两个实例分别对应 9502 和 9503且健康状态都为绿色。步骤 4验证消费者发现双节点调用订单接口或文章详情接口都会远程调用用户服务curlhttp://localhost:9501/orders/1查看hyperf.log负载均衡器日志应显示类似RPC负载均衡可用节点: [{host:127.0.0.1,port:9502},{host:127.0.0.1,port:9503}] RPC请求路由到节点: 127.0.0.1:9502连续请求几次会看到交替路由到 9502 和 9503说明双节点负载均衡生效扩容成功。步骤 5模拟缩容——摘除一个节点暂时停止第二个用户服务节点我们可以通过修改server.php将jsonrpc2注释掉或者更简单在 Consul API 中手动将第二个节点标记为不健康。但为了真实我们热停止第二个 JSON-RPC 服务器端口。由于它们运行在同一个 Worker 进程中无法单独停止端口。我们可以临时注释掉server.php中的jsonrpc2配置然后重启服务来模拟缩容。但重启会导致短暂中断不符合“无感”。更好的方法是我们在另一个终端中手动向 Consul 发送该节点下线请求让 Consul 标记为 critical从而消费者会自动移除它。使用 API 下线 9503 实例首先需要知道该实例的 Service ID。在 Consul UI 中点击UserService可以看到每个实例的 ID通常是类名或随机 ID。例如可能是UserService-jsonrpc2-...。我们也可以通过 API 注销它# 列出 UserService 的所有实例curlhttp://consul:8500/v1/health/service/UserService# 找到对应 9503 的 ServiceID然后调用注销curl-XPUT http://consul:8500/v1/agent/service/deregister/ServiceID注销后消费者会在下一次刷新5 秒内更新节点列表之后请求只会路由到 9502。观察日志可用节点列表变成只有一个。无感验证在注销前后持续发送请求# 在另一个终端循环发送请求whiletrue;docurl-shttp://localhost:9501/orders/1|jq.;sleep0.2;done在注销瞬间可能会有一次请求失败如果恰好发往被注销的节点且该节点真的停止了服务但因为我们的 9503 服务实际并未真正停止进程只是注销了 Consul请求仍然能到达所以不会出错。若要真正模拟宕机可同时停止 9503 端口的监听但这里无法单独停止。不过我们主要展示的是消费者节点列表的变化这已经证明了动态感知能力。更极致的模拟使用两个独立的 Docker 容器分别运行不同的 Hyperf 实例这样就能真正停止一个容器。受限于环境我们通过 Consul 注销来等效演示。步骤 6结合 Nacos 动态调整限流在 Nacos 控制台dev 命名空间修改配置将rate_limit.like_per_minute从 10 改为 3发布。无需重启服务立即测试点赞接口foriin{1..5};docurl-XPOST http://localhost:9501/articles/1/like;echo;sleep0.5;done前 3 次应该成功可能受之前计数影响之后返回 429证明动态限流生效。这就是配置中心的威力。步骤 7全链路压测与观察使用ab对订单详情接口进行 200 并发请求查看日志中的负载均衡分布、错误率ab-n200-c20http://localhost:9501/orders/1观察请求失败数应为 0。日志中两个用户服务节点被均匀路由如果都在线。熔断器没有打开。连接池工作正常。然后模拟缩容注销一个节点再次压测观察错误率由于负载均衡器会选择剩余节点可能刚开始会有个别连接失败如果注销的同时有请求正在发往那个节点且节点已拒绝但很快稳定错误率应该接近 0。说明具有高可用性。四、成果测试与检验约 1 小时测试清单检验项方法通过标准双节点注册Consul UI 或 API 查看UserService两个实例端口 9502 和 9503健康消费者发现双节点查看hyperf.log负载均衡器日志可用节点列表包含两个节点请求轮询到两个节点缩容自动剔除注销 9503 节点等待刷新再次请求日志中节点列表只剩 9502请求不再路由到 9503配置动态更新修改 Nacos 中的限流值测试点赞新阈值生效无重启无感扩容/缩容持续发送请求过程中执行缩容操作请求失败率 1%理想 0全链路压测ab压测订单接口QPS 稳定错误率 0熔断未触发常见问题与定位第二个节点没有注册到 Consul检查UserService2类的#[RpcService]注解是否正确server名称是否与server.php中的配置一致检查是否触发了 Composer 自动加载可能需要composer dump-autoload。负载均衡器日志不打印检查日志级别是否为INFO确认CustomRoundRobin的select是否被调用。配置中心更新延迟Nacos 客户端长轮询间隔可临时调为 1 秒更快感知变化。端口冲突确保 9503 未被其他服务占用。五、今日作业与学习产出提交代码将UserService2、修改后的server.php等提交到 Git附上今天的测试截图或日志示例。绘制架构演进图对比最初硬编码节点 → 注册中心 手动多节点 → 动态伸缩的演变过程。画出完整的服务调用链路图包含 Nacos 配置下发和 Consul 节点变更的流程。学习笔记总结服务无感扩容的关键要素注册中心、健康检查、客户端负载均衡、配置中心协同。思考如果注册中心本身宕机消费者还能调用服务吗答案可以因为消费者有本地缓存。挑战任务使用Docker Compose 扩容命令启动多个 PHP 容器实例不同端口作为独立的用户服务节点观察 Consul 中自动出现多个实例并测试真实缩容docker stop一个容器时的无感效果。尝试使用 Nacos 的灰度发布功能仅对特定消费者下发新配置。恭喜你完成了第四周的全部内容经过这一周你的微服务已经具备了动态服务治理的能力服务能够自动注册和发现配置可实时变更实例可弹性伸缩整个系统向着生产级迈进了一大步。下周我们将进入 API 网关的世界将所有这些后端服务统一管理起来。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进