资讯动态

【基于 Swoole+Hyperf 的微服务实战】 第四周·周二:基于 Consul 的服务发现与负载均衡

发布时间:2026/9/12 18:58:22 来源:尧图企业网站定制
【基于 SwooleHyperf 的微服务实战】 第四周·周二基于 Consul 的服务发现与负载均衡今天我们进入的主题是基于 Consul 的服务发现与负载均衡。昨天我们已经成功将用户服务和商品服务注册到 Consul今天要让消费者文章服务、订单服务动态地从注册中心获取可用节点彻底告别硬编码的nodes列表并结合自定义负载均衡器实现弹性伸缩的调用能力。今日目标理解服务发现的核心流程消费者如何从注册中心拉取并维护最新节点列表。改造 RPC 消费者配置使用 Consul 驱动自动发现UserService和ProductService。验证动态发现效果通过 Consul UI 增加/删除节点观察消费者负载均衡日志的变化。配置消费者端的节点刷新策略确保实例变动能及时感知。结合自定义CustomRoundRobin负载均衡器在动态节点上实现轮询。一、环境准备约 15 分钟继续使用hyperf-app项目和 Docker 环境确保 Consul、MySQL、Redis 等服务都已启动。docker-composeexecswoolebashcd/var/www/hyperf-app为了演示多节点动态发现我们今天将保留一个用户服务节点9502和一个商品服务节点9504但会通过 Consul 手动注册一个额外的模拟节点来展示负载均衡效果。二、知识核心服务发现原理与 Consul 集成约 1 小时1. 服务发现的工作方式服务发现分为客户端发现和服务端发现两种模式。我们使用的是客户端发现也是 Consul 的典型用法服务消费者内置了服务发现客户端直接从注册中心Consul获取所有可用服务实例的列表。负载均衡在客户端完成消费者根据策略选择一个实例发起请求。健康检查由注册中心维护不健康的实例不会被返回给消费者。节点列表更新消费者不会每次调用都去注册中心查询而是定期拉取或长轮询/监听更新本地缓存。Consul 支持 blocking query长轮询HTTP API 的index参数可以等待变更。Hyperf 的 Consul 驱动默认会定时可配置refresh_time拉取并更新本地节点缓存。2. Hyperf 消费者配置迁移之前我们的services.php中每个消费者都硬编码了nodesconsumers[[nameUserService,nodes[[host127.0.0.1,port9502],],// ...],]今天我们要去掉nodes改为指定使用注册中心进行发现consumers[[nameUserService,registry[protocolconsul,addressenv(CONSUL_URI,http://consul:8500),],load_balancerApp\LoadBalancer\CustomRoundRobin::class,options[/* ... */],],]框架会根据registry配置实例化对应的发现驱动ConsulDriver在第一次调用时从 Consul 拉取服务列表并定时刷新。3. 消费者节点刷新配置在消费者配置中可以增加refresh_time参数控制刷新间隔单位秒默认可能是 10 秒或 30 秒。我们将在实战中配置为 5 秒以便观察效果。三、实战改造消费者实现动态发现约 2.5 小时步骤 1修改消费者配置编辑config/autoload/services.php将consumers数组里的用户服务和商品服务配置改为使用注册中心。原配置保留熔断、超时等部分consumers[// 用户服务消费者[nameUserService,// 去掉 nodes 和 load_balancer 位置不变load_balancerApp\LoadBalancer\CustomRoundRobin::class,registry[protocolconsul,addressenv(CONSUL_URI,http://consul:8500),],// 刷新间隔秒refresh_time5,options[connect_timeout2.0,recv_timeout2.0,retry_count1,retry_interval100,pool[min_connections1,max_connections10],circuit_breakertrue,circuit_breaker_options[failure_count3,open_timeout30,half_open_limit1,],],],// 商品服务消费者[nameProductService,load_balancerApp\LoadBalancer\CustomRoundRobin::class,registry[protocolconsul,addressenv(CONSUL_URI,http://consul:8500),],refresh_time5,options[connect_timeout2.0,recv_timeout2.0,retry_count1,pool[min_connections1,max_connections10],circuit_breakertrue,circuit_breaker_options[failure_count3,open_timeout30,],],],],drivers部分的 Consul 配置昨天已添加保持不变。步骤 2确保服务提供者正确注册在server.php中保留jsonrpc(9502) 和jsonrpc-product(9504) 两个服务注释掉多余的或保留但确保它们注册了。我们昨天只保留了单节点为了今天演示动态发现的多节点效果可以通过Consul 手动注册一个额外的实例来模拟扩容而不必启动真的第二个 Hyperf 进程。手动注册一个模拟节点使用 curl 在容器内操作# 注册一个额外的用户服务节点端口 9503并不真正监听curl-XPUT http://consul:8500/v1/agent/service/register\-HContent-Type: application/json\-d{ ID: user-service-9503, Name: UserService, Address: 127.0.0.1, Port: 9503, Check: { TTL: 30s, DeregisterCriticalServiceAfter: 1m } }注意因为 9503 没有真实服务在监听健康检查会失败最终会被自动注销。为了让节点在列表中保留一段时间我们需要给 TTL 发送心跳或者暂时使用“不健康节点”但不自动注销将DeregisterCriticalServiceAfter设置得很大。但为了演示负载均衡选择健康节点我们应该保持健康节点。如果不启动真正的 9503 服务可以暂时通过 Consul API 注册一个没有健康检查的服务无 Check这样它会始终在列表中。更简单我们启动一个额外的 Hyperf 实例可以另开一个终端用不同端口启动另一个 Hyperf 进程但需要共享代码和依赖可能复杂。为了教学方便我们可以暂时给用户服务消费者配置一个额外的静态节点混合使用nodes和registry不推荐。我们换一种方式暂时恢复用户服务的多个节点但在消费者端依然使用注册中心只是我们在 Consul 中手动注册一个健康检查通过但实际不存在的节点可以用Passing状态强制设置。或者直接展示当只有一个节点时负载均衡器也能正常工作并观察日志验证节点来自 Consul。我们可以这样设计今天的主要目标是验证消费者成功从 Consul 获取到节点即使只有一个节点我们也能在日志中看到负载均衡器打印的节点信息带端口证明不是从硬编码获得的。然后通过修改 Consul 注册信息如添加另一个实例并等待refresh_time后消费者日志中会出现新节点说明动态发现生效。这样就能充分展示动态发现能力。步骤 3修改 CustomRoundRobin 日志确保CustomRoundRobin的select方法中打印日志以便观察节点来源publicfunctionselect(array$nodes):Node{$logger\Hyperf\Utils\ApplicationContext::getContainer()-get(\Psr\Log\LoggerInterface::class);$logger-info(sprintf(RPC负载均衡可用节点: %s,json_encode(array_map(fn($n)$n-host.:.$n-port,$nodes))));// ... 原有轮询逻辑$node$nodes[$this-currentIndex%count($nodes)];$logger-info(sprintf(RPC请求路由到节点: %s:%d,$node-host,$node-port));// ...}步骤 4重启并测试基本调用php bin/hyperf.php start确保服务启动Consul UI 中UserService和ProductService存在且健康。调用订单接口curlhttp://localhost:9501/orders/1查看hyperf.log应出现类似[INFO] RPC负载均衡可用节点: [{host:127.0.0.1,port:9502}] [INFO] RPC请求路由到节点: 127.0.0.1:9502证明消费者从 Consul 获取了节点并没有依赖nodes静态配置。步骤 5动态增加节点观察刷新效果手动在 Consul 中注册另一个健康节点模拟扩容curl-XPUT http://consul:8500/v1/agent/service/register\-HContent-Type: application/json\-d{ ID: user-service-extra, Name: UserService, Address: 127.0.0.1, Port: 9600, Check: { TTL: 10s, DeregisterCriticalServiceAfter: 30s } }但我们必须发送 TTL 心跳让这个节点健康否则消费者只会拉取健康的实例。如果不发送心跳几秒后节点变成 critical不会出现在健康实例列表。我们可以临时设置Check: null不加健康检查这样 Consul 会认为节点不需要检查始终被视为健康。但 Consul 强烈建议有检查这里为了演示可以使用无检查模式curl-XPUT http://consul:8500/v1/agent/service/register\-HContent-Type: application/json\-d{ ID: user-service-extra, Name: UserService, Address: 127.0.0.1, Port: 9600 }这样注册的服务没有健康检查会一直显示为passing绿色。等待约 5 秒refresh_time再次调用订单接口查看日志[INFO] RPC负载均衡可用节点: [{host:127.0.0.1,port:9502},{host:127.0.0.1,port:9600}] [INFO] RPC请求路由到节点: 127.0.0.1:9502 [INFO] RPC请求路由到节点: 127.0.0.1:9600 # 第二次或第三次请求可见新增的节点已经被发现并参与轮询。如果实际没有服务监听 9600请求会失败但由于有重试和熔断最终会降级。你可以观察降级日志。步骤 6动态剔除节点通过 API 手动删除 extra 节点curl-XPUT http://consul:8500/v1/agent/service/deregister/user-service-extra等待刷新再次请求日志中可用节点列表恢复为一个。这表明服务下线能被自动感知。四、成果测试与验证约 1 小时测试清单检验项方法通过标准消费者不再使用静态 nodes检查services.php中 consumers 无nodes字段配置正确服务从 Consul 发现启动后调用接口日志显示从 Consul 获取的节点列表日志包含节点信息且端口与注册一致动态新增节点通过 API 注册新节点等待刷新后调用日志中节点列表包含新节点负载均衡器路由到新节点若健康动态剔除节点删除新节点等待刷新节点列表中不再包含该节点负载均衡策略多次调用观察路由日志轮询生效有多个节点时交替与熔断降级协同新节点不可用时调用失败后触发熔断降级返回默认数据降级生效日志显示异常常见问题刷新未生效检查refresh_time配置是否生效可调低至 3 秒。也可能是缓存可尝试重启服务。Consul 服务发现失败检查CONSUL_URI可达性telnet consul 8500。查看框架日志中是否有 Consul 连接错误。节点列表为空确保服务提供者已注册且健康。若使用无检查方式注意实例 ID 不要冲突。五、今日作业与学习产出提交代码将修改后的services.php、CustomRoundRobin.php提交到 Git。完善负载均衡在CustomRoundRobin中增加节点权重读取从 Consul 的Meta或通过配置实现加权轮询。添加一个least_connections负载均衡器简单模拟观察在多节点时的行为。学习笔记画出服务发现的完整时序图Provider 注册 → Consumer 首次拉取 → 定时刷新 → 请求负载均衡。对比硬编码、手动配置、注册中心三种方式各自的适用场景和优缺点。挑战任务研究hyperf/service-governance-consul的源码理解ConsulDriver如何实现getNodes方法。尝试配置Consul的blocking query长轮询以代替定时刷新减少延迟需要修改驱动配置可参考官方文档。启动第二个真正的 Hyperf 实例使用不同端口和不同容器名作为用户服务的第二个节点观察完整的自动注册和发现过程。通过今天的学习你的微服务架构已经具备了动态伸缩的雏形服务实例可以随意增减消费者无需重启即可感知变化。明天我们将引入配置中心Nacos 或 Apollo把限流阈值、熔断参数等动态化让系统更加灵活。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价