服务注册与发现完全指南
服务注册与发现完全指南前言在微服务架构中服务注册与发现是实现服务间通信的基础设施。服务注册中心维护着所有服务的实例信息使得服务消费者能够动态地发现和调用服务提供者。本文将详细介绍服务注册与发现的核心概念、实现机制以及最佳实践。一、服务注册与发现概述1.1 为什么需要服务注册与发现在微服务架构中服务实例的网络位置是动态分配的传统的IP端口方式无法满足需求。服务注册与发现机制通过以下方式解决问题服务注册服务启动时向注册中心注册自己的网络位置心跳检测定期向注册中心发送心跳维持注册状态服务发现消费者从注册中心获取可用的服务实例列表1.2 核心组件┌─────────────────────────────────────────────────────┐ │ Service Registry Architecture │ │ │ │ ┌─────────────┐ │ │ │ Registry │◄─────── Register ───────┐ │ │ │ Center │ │ │ │ └──────┬──────┘ │ │ │ │ │ │ │ │ Heartbeat │ │ │ ▼ │ │ │ ┌─────────────┐ │ │ │ │ Service │◄─────── Query ───────────┤ │ │ │ Consumer │ │ │ │ └─────────────┘ │ │ │ │ │ │ ┌─────────────┐ │ │ │ │ Service │──────────────────────────┘ │ │ │ Provider │ │ │ └─────────────┘ │ └─────────────────────────────────────────────────────┘二、Eureka服务注册与发现2.1 Eureka Server配置# application.yml server: port: 8761 spring: application: name: eureka-server eureka: instance: hostname: localhost client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ server: enable-self-preservation: true eviction-interval-timer-in-ms: 60000 renewal-percent-threshold: 0.85SpringBootApplication EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }2.2 Eureka Client配置# application.yml spring: application: name: product-service eureka: instance: hostname: ${HOSTNAME:localhost} ip-address: ${POD_IP:127.0.0.1} non-secure-port: ${SERVER_PORT:8080} instance-id: ${spring.application.name}:${eureka.instance.hostname}:${server.port} status-page-url-path: /actuator/info health-check-url-path: /actuator/health lease-renewal-interval-in-seconds: 10 lease-expiration-duration-in-seconds: 30 client: register-with-eureka: true fetch-registry: true service-url: defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka/} registry-fetch-interval-seconds: 30 healthcheck: enabled: trueSpringBootApplication EnableDiscoveryClient public class ProductServiceApplication { public static void main(String[] args) { SpringApplication.run(ProductServiceApplication.class, args); } }2.3 自定义元数据eureka: instance: metadata-map: version: v1.0.0 environment: production >Service public class CustomMetadataService { Autowired private EurekaClient eurekaClient; public ListServiceInstance getInstancesWithMetadata(String serviceName) { ListInstanceInfo instances eurekaClient .getInstancesByVipAddress(serviceName, false); return instances.stream() .filter(info - production.equals( info.getMetadata().get(environment))) .map(info - new DefaultServiceInstance( info.getInstanceId(), info.getHostName(), info.getPort(), false, info.getMetadata())) .collect(Collectors.toList()); } }三、Nacos服务注册与发现3.1 Nacos Server配置# application.yml server: port: 8848 spring: application: name: nacos-server nacos: server: addr: localhost:8848 config: server-addr: ${spring.cloud.nacos.server} file-extension: yaml discovery: server-addr: ${spring.cloud.nacos.server}3.2 Nacos Client配置spring: application: name: order-service cloud: nacos: discovery: enabled: true server-addr: localhost:8848 namespace: ${NACOS_NAMESPACE:public} group: ${NACOS_GROUP:DEFAULT_GROUP} cluster-name: ${NACOS_CLUSTER:DEFAULT} weight: 1 instance-id: ${spring.application.name}:${random.int} metadata: version: v1.0.0 environment: production register-enabled: true heart-beat-interval: 5000 heart-beat-timeout: 15000 ip-delete-timeout: 300003.3 动态服务发现Service Slf4j public class NacosServiceDiscovery { Autowired private NamingService namingService; public ListInstance getInstances(String serviceName) { try { return namingService.selectInstances(serviceName, true); } catch (NacosException e) { log.error(Failed to get instances, e); return Collections.emptyList(); } } public Instance getOneHealthyInstance(String serviceName) { try { return namingService.selectOneHealthyInstance(serviceName); } catch (NacosException e) { log.error(Failed to get healthy instance, e); return null; } } public ListInstance getInstancesByCluster(String serviceName, String cluster) { try { return namingService.selectInstances(serviceName, cluster, true); } catch (NacosException e) { log.error(Failed to get instances by cluster, e); return Collections.emptyList(); } } // 订阅服务变化 public void subscribeServiceChange(String serviceName, EventListener eventListener) { try { namingService.subscribe(serviceName, eventListener); } catch (NacosException e) { log.error(Failed to subscribe service, e); } } }四、Consul服务注册与发现4.1 Consul Agent配置# consul.d/server.json { datacenter: dc1, data_dir: /var/consul, ui_config: { enabled: true }, server: true, bootstrap_expect: 3, addresses: { https: 0.0.0.0 }, ports: { http: 8500, https: -1, dns: 8600 }, enable_script_checks: false, connect: { enabled: true } }4.2 Consul Client配置spring: application: name: payment-service cloud: consul: host: localhost port: 8500 discovery: enabled: true prefer-ip-address: true instance-id: ${spring.application.name}:${random.value} service-name: ${spring.application.name} health-check-path: /actuator/health health-check-interval: 10s deregister: true register: true tags: - version1.0.0 - environmentproduction4.3 Consul服务查询Service public class ConsulServiceDiscovery { Autowired private ConsulClient consulClient; public ListCatalogServiceEntry getServices() { return consulClient.getCatalogServices( Query.newQueryParamsBuilder().build() ).getValue().values().stream() .flatMap(entry - getServiceInstances(entry.getServiceName()).stream()) .collect(Collectors.toList()); } public ListCatalogServiceEntry getServiceInstances(String serviceName) { return consulClient.getCatalogServiceInstances( serviceName, Query.newQueryParamsBuilder().build() ).getValue(); } public ResponseListHealthService getHealthyInstances(String serviceName) { return consulClient.healthServiceInstances( serviceName, true, Query.newQueryParamsBuilder().build() ); } }五、服务发现负载均衡5.1 Ribbon负载均衡Configuration public class RibbonConfig { Bean public IRule ribbonRule() { // 区域感知负载均衡 return new ZoneAvoidanceRule(); } Bean public IPing ribbonPing() { return new PingUrl(false, /actuator/health); } Bean public ServerListSubsetFilterServer serverListFilter() { ServerListSubsetFilterServer filter new ServerListSubsetFilter(); filter.setSize(10); return filter; } } // 自定义负载均衡策略 Configuration public class CustomLoadBalancerConfig { Bean public IRule customRule() { return new WeightedResponseTimeRule(); } } // 基于权重的负载均衡 Component public class WeightedLoadBalancer { private MapString, Double serviceWeights new ConcurrentHashMap(); public void updateWeight(String serviceId, double weight) { serviceWeights.put(serviceId, weight); } public ServiceInstance selectServer(ListServiceInstance instances) { if (instances.isEmpty()) { return null; } double totalWeight instances.stream() .mapToDouble(instance - { String key instance.getServiceId() : instance.getHost(); return serviceWeights.getOrDefault(key, 1.0); }) .sum(); double randomValue Math.random() * totalWeight; double currentWeight 0; for (ServiceInstance instance : instances) { String key instance.getServiceId() : instance.getHost(); double weight serviceWeights.getOrDefault(key, 1.0); currentWeight weight; if (currentWeight randomValue) { return instance; } } return instances.get(0); } }5.2 LoadBalancer集成Configuration public class LoadBalancerConfig { Bean public ReactorLoadBalancerServiceInstance randomServiceInstanceLoadBalancer( Environment environment) { String name environment.getProperty( SpringCloudLoadBalancerClientConfiguration.LOADBALANCER_NAME_PROPERTY_NAME); return new RandomLoadBalancer( new ObjectProviderServiceInstance() { Override public StreamServiceInstance getIfAvailable( StreamServiceInstance elements) { return elements; } Override public ServiceInstance getIfAvailable() { return null; } Override public void ifAvailable(ConsumerServiceInstance action) { action.accept(null); } }, name ); } } // 使用LoadBalancer Service public class ProductClientService { Autowired private LoadBalancerClient loadBalancer; public String getProductInfo(Long productId) { ServiceInstance instance loadBalancer.choose(product-service); String url String.format(http://%s:%d/api/products/%d, instance.getHost(), instance.getPort(), productId); return restTemplate.getForObject(url, String.class); } }六、服务健康检查6.1 健康检查端点Component public class CustomHealthIndicator implements ReactiveHealthIndicator { private final MapString, HealthStatus serviceStatuses new ConcurrentHashMap(); Override public MonoHealth health() { return checkDependencies() .map(depsHealth - { if (depsHealth.getStatus().equals(Status.UP)) { return Health.up() .withDetail(services, serviceStatuses) .build(); } else { return Health.down() .withDetail(services, serviceStatuses) .withDetail(failedDependencies, depsHealth.getDetails()) .build(); } }) .onErrorResume(e - Mono.just( Health.down() .withException(e) .build() )); } private MonoHealth checkDependencies() { // 检查依赖服务 return Flux.fromIterable(serviceStatuses.entrySet()) .flatMap(entry - checkServiceHealth(entry.getKey())) .reduce(Health.up(), (health, status) - { if (status ! Status.UP) { return Health.down().build(); } return health; }); } }6.2 心跳续约机制Service public class HeartbeatService { Autowired private InstanceRenewer instanceRenewer; Scheduled(fixedRate 30000) public void sendHeartbeat() { try { instanceRenewer.renew( order-service, order-service:localhost:8080 ); } catch (Exception e) { log.error(Failed to send heartbeat, e); } } }七、最佳实践7.1 服务注册配置# 生产环境推荐配置 eureka: instance: lease-renewal-interval-in-seconds: 10 lease-expiration-duration-in-seconds: 30 prefer-ip-address: true instance-id: ${spring.application.name}:${random.value} client: registry-fetch-interval-seconds: 10 register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka-1:8761/eureka/,http://eureka-2:8762/eureka/ server: enable-self-preservation: true renewal-percent-threshold: 0.85 eviction-interval-timer-in-ms: 600007.2 优雅下线Service public class GracefulShutdownService { Autowired private EurekaClient eurekaClient; PreDestroy public void deregister() { log.info(Starting graceful shutdown); // 延迟确保流量已切换 try { Thread.sleep(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 下线服务 eurekaClient.shutdown(); log.info(Service deregistered from Eureka); } }八、总结服务注册与发现是微服务架构的基础设施。通过合理配置注册中心、优化负载均衡策略、实施健康检查机制可以构建高可用的服务发现体系。不同的注册中心Eureka、Nacos、Consul各有特点应根据实际需求选择合适的方案。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2635192.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!