Hyperf方案 服务注册与发现
?php/** * 案例标题服务注册与发现 * 说明基于Consul实现微服务注册、注销和健康检查服务启动时自动注册关闭时自动注销 * 需要安装的包 * composer require hyperf/service-governance * composer require hyperf/service-governance-consul * composer require hyperf/guzzle */declare(strict_types1);// config/autoload/services.php // 服务治理配置告诉框架用Consul做注册中心return[drivers[consul[urienv(CONSUL_URI,http://127.0.0.1:8500),// Consul地址],],];// app/Service/ConsulRegistrar.php namespaceApp\Service;useGuzzleHttp\Client;useHyperf\Contract\ConfigInterface;usePsr\Log\LoggerInterface;/** * 手动封装Consul注册逻辑方便自定义健康检查路径和服务元信息 */classConsulRegistrar{privateClient$http;// HTTP客户端用来调Consul APIprivatestring$serviceId;// 服务唯一ID用于注销时找到自己publicfunction__construct(privateConfigInterface$config,privateLoggerInterface$logger){$consulUrienv(CONSUL_URI,http://127.0.0.1:8500);$this-httpnewClient([base_uri$consulUri,timeout5.0,// 5秒超时别让注册阻塞太久]);// 服务ID用 服务名-IP-端口 拼保证同一台机器起多个实例也不冲突$this-serviceIdsprintf(%s-%s-%d,env(SERVICE_NAME,hyperf-app),$this-getLocalIp(),env(SERVICE_PORT,9501));}/** * 服务启动时调这个把自己注册到Consul */publicfunctionregister():void{$payload[ID$this-serviceId,Nameenv(SERVICE_NAME,hyperf-app),// 服务名同名多个实例会自动负载均衡Address$this-getLocalIp(),// 服务IPConsul用这个找你Port(int)env(SERVICE_PORT,9501),// 监听端口Tags[hyperf,env(APP_ENV,dev)],// 标签方便过滤Meta[versionenv(APP_VERSION,1.0.0),// 版本号灰度发布会用到],Check[// 健康检查Consul每隔10秒来戳一下这个HTTP接口HTTPsprintf(http://%s:%d/health,$this-getLocalIp(),env(SERVICE_PORT,9501)),Interval10s,// 检查间隔Timeout5s,// 检查超时DeregisterCriticalServiceAfter30s,// 连续失败30秒后自动注销防止僵尸服务],];try{$this-http-put(/v1/agent/service/register,[json$payload,]);$this-logger-info(服务注册成功,[service_id$this-serviceId]);}catch(\Exception$e){// 注册失败只记日志不影响服务正常启动$this-logger-error(服务注册失败: .$e-getMessage());}}/** * 服务停止时调这个主动从Consul摘掉自己 * 不主动注销的话要等健康检查失败才能自动清掉有延迟 */publicfunctionderegister():void{try{$this-http-put(/v1/agent/service/deregister/{$this-serviceId});$this-logger-info(服务注销成功,[service_id$this-serviceId]);}catch(\Exception$e){$this-logger-error(服务注销失败: .$e-getMessage());}}/** * 发现某个服务的所有健康实例返回地址列表 * 调用其他微服务前先来这里查一下地址 */publicfunctiondiscover(string$serviceName):array{try{$response$this-http-get(/v1/health/service/{$serviceName},[query[passingtrue],// 只返回健康的实例]);$servicesjson_decode($response-getBody()-getContents(),true);// 把Consul返回的数据整理成简洁的地址列表returnarray_map(function($item){return[address$item[Service][Address],port$item[Service][Port],version$item[Service][Meta][version]??unknown,];},$services);}catch(\Exception$e){$this-logger-error(服务发现失败: .$e-getMessage());return[];}}/** * 获取本机内网IP注册到Consul的地址 */privatefunctiongetLocalIp():string{// 先看环境变量有没有手动配Docker里容器IP不一定准if($ipenv(SERVICE_HOST,)){return$ip;}// 没配就自动取获取eth0或第一个非回环地址$ipgethostbyname(gethostname());return$ip?:127.0.0.1;}}// app/Listener/RegisterServiceListener.php namespaceApp\Listener;useApp\Service\ConsulRegistrar;useHyperf\Event\Contract\ListenerInterface;useHyperf\Framework\Event\MainWorkerStart;/** * 监听Worker启动事件主Worker起来就注册服务 */classRegisterServiceListenerimplementsListenerInterface{publicfunction__construct(privateConsulRegistrar$registrar){}publicfunctionlisten():array{return[MainWorkerStart::class];// 只在主Worker启动时注册一次}publicfunctionprocess(object$event):void{$this-registrar-register();// 服务启动去Consul报到}}