跳到主要内容

使用 Consul 进行服务发现

DeepSeek V3 中英对照 Service Discovery with Consul

服务发现是基于微服务架构的关键原则之一。尝试手动配置每个客户端或采用某种形式的约定可能非常困难,并且可能非常脆弱。Consul 通过 HTTP API 和 DNS 提供服务发现服务。Spring Cloud Consul 利用 HTTP API 进行服务注册和发现。这并不妨碍非 Spring Cloud 应用程序利用 DNS 接口。Consul 代理服务器运行在一个通过 gossip 协议进行通信并使用 Raft 共识协议的集群中。

如何激活​

要启用 Consul 服务发现,请使用组为 org.springframework.cloud、工件 ID 为 spring-cloud-starter-consul-discovery 的启动器。有关使用当前 Spring Cloud Release Train 设置构建系统的详细信息,请参阅 Spring Cloud 项目页面。

向 Consul 注册​

当客户端向Consul注册时,它会提供自身的元数据,例如主机和端口、ID、名称和标签。默认情况下会创建一个HTTP检查,Consul每10秒访问一次/actuator/health端点。如果健康检查失败,服务实例将被标记为严重状态。

示例 Consul 客户端:

@SpringBootApplication
@RestController
public class Application {

@RequestMapping("/")
public String home() {
return "Hello world";
}

public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}

}

(即完全正常的 Spring Boot 应用)。如果 Consul 客户端不在 localhost:8500,则需要配置以定位客户端。示例:

spring:
cloud:
consul:
host: localhost
port: 8500
警告

如果你使用 Spring Cloud Consul Config,并且设置了 spring.cloud.bootstrap.enabled=true 或 spring.config.use-legacy-processing=true 或使用了 spring-cloud-starter-bootstrap,那么上述值需要放在 bootstrap.yml 中,而不是 application.yml。

默认的服务名称、实例ID和端口分别取自 Environment,分别为 ${spring.application.name}、Spring上下文ID和 ${server.port}。

要禁用 Consul 发现客户端,您可以将 spring.cloud.consul.discovery.enabled 设置为 false。当 spring.cloud.discovery.enabled 设置为 false 时,Consul 发现客户端也将被禁用。

要禁用服务注册,你可以将 spring.cloud.consul.discovery.register 设置为 false。

将管理功能注册为独立服务​

当管理服务器端口设置为与应用程序端口不同的值时,通过设置 management.server.port 属性,管理服务将作为独立于应用程序服务的单独服务进行注册。例如:

spring:
application:
name: myApp
management:
server:
port: 4452

上述配置将注册以下 2 个服务:

  • Application Service:
ID: myApp
Name: myApp
  • 管理服务:
ID: myApp-management
Name: myApp-management

管理服务将从应用服务继承其 instanceId 和 serviceName。例如:

spring:
application:
name: myApp
management:
server:
port: 4452
spring:
cloud:
consul:
discovery:
instance-id: custom-service-id
serviceName: myprefix-${spring.application.name}

以上配置将注册以下 2 个服务:

  • Application Service:
ID: custom-service-id
Name: myprefix-myApp
  • 管理服务:
ID: custom-service-id-management
Name: myprefix-myApp-management

可通过以下属性进行进一步自定义:

/** Port to register the management service under (defaults to management port) */
spring.cloud.consul.discovery.management-port

/** Suffix to use when registering management service (defaults to "management") */
spring.cloud.consul.discovery.management-suffix

/** Tags to use when registering management service (defaults to "management") */
spring.cloud.consul.discovery.management-tags

HTTP 健康检查​

Consul 实例的健康检查默认指向 "/actuator/health",这是 Spring Boot Actuator 应用中健康端点的默认位置。即使在使用 Actuator 应用的情况下,如果你使用了非默认的上下文路径或 servlet 路径(例如 server.servletPath=/foo)或管理端点路径(例如 management.server.servlet.context-path=/admin),你也需要更改此设置。

Consul 用于检查健康端点的间隔时间也可以进行配置。"10s" 和 "1m" 分别代表 10 秒和 1 分钟。

此示例说明了上述内容(更多选项请参阅附录页面中的 spring.cloud.consul.discovery.health-check-* 属性)。

spring:
cloud:
consul:
discovery:
healthCheckPath: ${management.server.servlet.context-path}/actuator/health
healthCheckInterval: 15s

你可以通过设置 spring.cloud.consul.discovery.register-health-check=false 来完全禁用 HTTP 健康检查。

应用头部信息​

可以在健康检查请求中添加请求头。例如,如果您尝试注册一个使用 Vault Backend 的 Spring Cloud Config 服务器:

spring:
cloud:
consul:
discovery:
health-check-headers:
X-Config-Token: 6442e58b-d1ea-182e-cfa5-cf9cddef0722

根据 HTTP 标准,每个标头可以包含多个值,在这种情况下,可以提供一个数组:

spring:
cloud:
consul:
discovery:
health-check-headers:
X-Config-Token:
- "6442e58b-d1ea-182e-cfa5-cf9cddef0722"
- "Some other value"

TTL 健康检查​

除了默认配置的HTTP检查外,还可以使用Consul的TTL检查。主要区别在于,应用程序会向Consul代理发送心跳信号,而不是由Consul代理向应用程序发送请求。

应用程序发送 ping 的间隔时间也可以配置。"10s" 和 "1m" 分别代表 10 秒和 1 分钟。默认值为 30 秒。

此示例说明了上述内容(更多选项请参阅附录页面中的 spring.cloud.consul.discovery.heartbeat.* 属性)。

spring:
cloud:
consul:
discovery:
heartbeat:
enabled: true
ttl: 10s

TTL 应用状态​

对于Spring Boot Actuator应用程序,其状态由其可用的健康端点决定。当健康端点不可用(无论是被禁用还是非Spring Boot Actuator应用程序)时,系统会假定应用程序处于健康状态。

查询健康端点时,默认使用根健康组。可以通过设置以下属性来使用不同的健康组:

spring:
cloud:
consul:
discovery:
heartbeat:
actuator-health-group: <your-custom-group-goes-here>

你可以通过设置以下属性来完全禁用健康端点的使用:

spring:
cloud:
consul:
discovery:
heartbeat:
use-actuator-health: false
自定义TTL应用状态​

如果你想配置自己的应用程序状态机制,只需实现 ApplicationStatusProvider 接口

@Bean
public class MyCustomApplicationStatusProvider implements ApplicationStatusProvider {
public CheckStatus currentStatus() {
return yourMethodToDetermineAppStatusGoesHere();
}
}

并将其提供给应用程序上下文:

@Bean
public CustomApplicationStatusProvider customAppStatusProvider() {
return new MyCustomApplicationStatusProvider();
}

执行器健康指示器​

如果服务实例是一个 Spring Boot Actuator 应用,它可能会提供以下 Actuator 健康指标。

DiscoveryClientHealthIndicator​

当 Consul 服务发现功能启用时,系统会配置一个 DiscoverClientHealthIndicator,并使其在 Actuator 健康检查端点中可用。有关配置选项,请参阅此处。

ConsulHealthIndicator​

配置了一个指标,用于验证 ConsulClient 的健康状况。

默认情况下,它会获取Consul领导者节点状态和所有已注册的服务。在拥有大量已注册服务的部署中,每次健康检查都获取所有服务可能成本较高。要跳过服务检索,仅检查领导者节点状态,请设置 spring.cloud.consul.health-indicator.include-services-query=false。

要禁用该指示器,请设置 management.health.consul.enabled=false。

注意

当应用程序在 bootstrap context mode(默认模式)下运行时,此指标会被加载到引导上下文中,并且不会提供给 Actuator 健康端点使用。

Metadata​

Consul 支持在服务上设置元数据。Spring Cloud 的 ServiceInstance 包含一个 Map<String, String> metadata 字段,该字段会从服务的 meta 字段中填充。要填充 meta 字段,请在 spring.cloud.consul.discovery.metadata 或 spring.cloud.consul.discovery.management-metadata 属性中设置值。

spring:
cloud:
consul:
discovery:
metadata:
myfield: myvalue
anotherfield: anothervalue

上述配置将生成一个服务,其元数据字段包含 myfield→myvalue 和 anotherfield→anothervalue。

生成的元数据​

Consul 自动注册将自动生成一些条目。

表 1. 自动生成的元数据

键值
'group'属性 spring.cloud.consul.discovery.instance-group。仅当 instance-group 不为空时才会生成此值。'
'secure'如果属性 spring.cloud.consul.discovery.scheme 等于 'https' 则为 true,否则为 false。
属性 spring.cloud.consul.discovery.default-zone-metadata-name,默认为 'zone'属性 spring.cloud.consul.discovery.instance-zone。仅当 instance-zone 不为空时才会生成此值。'
注意

旧版本的 Spring Cloud Consul 通过解析 spring.cloud.consul.discovery.tags 属性来填充 Spring Cloud Commons 中的 ServiceInstance.getMetadata() 方法。此方式已不再支持,请迁移至使用 spring.cloud.consul.discovery.metadata 映射。

使 Consul 实例 ID 唯一​

默认情况下,Consul实例会注册一个ID,该ID等于其Spring应用上下文ID。默认情况下,Spring应用上下文ID为${spring.application.name}:comma,separated,profiles:${server.port}。在大多数情况下,这将允许一个服务的多个实例在一台机器上运行。如果需要进一步保证唯一性,使用Spring Cloud,您可以通过在spring.cloud.consul.discovery.instanceId中提供唯一标识符来覆盖此设置。例如:

spring:
cloud:
consul:
discovery:
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}

有了这些元数据,以及部署在本地主机上的多个服务实例,随机值将在那里生效,以使实例具有唯一性。在 Cloudfoundry 中,Spring Boot 应用程序会自动填充 vcap.application.instance_id,因此将不需要随机值。

查找服务​

使用负载均衡器​

Spring Cloud 支持 Feign(一个 REST 客户端构建器)以及 Spring RestTemplate,用于通过逻辑服务名称/ID 而非物理 URL 来查找服务。Feign 和具备服务发现功能的 RestTemplate 都利用 Spring Cloud LoadBalancer 进行客户端负载均衡。

如果你想使用 RestTemplate 访问服务 STORES,只需声明:

@LoadBalanced
@Bean
public RestTemplate loadbalancedRestTemplate() {
return new RestTemplate();
}

并像这样使用它(注意我们使用的是Consul中的STORES服务名称/ID,而不是完全限定域名):

@Autowired
RestTemplate restTemplate;

public String getFirstProduct() {
return this.restTemplate.getForObject("https://STORES/products/1", String.class);
}

如果您在多个数据中心拥有Consul集群,并且想要访问另一个数据中心的服务,仅凭服务名称/ID是不够的。在这种情况下,您需要使用属性 spring.cloud.consul.discovery.datacenters.STORES=dc-west,其中 STORES 是服务名称/ID,而 dc-west 是 STORES 服务所在的数据中心。

提示

Spring Cloud 现在也提供了对 Spring Cloud LoadBalancer 的支持。

使用 DiscoveryClient​

你也可以使用 org.springframework.cloud.client.discovery.DiscoveryClient,它提供了一个不特定于Netflix的发现客户端的简单API,例如。

@Autowired
private DiscoveryClient discoveryClient;

public String serviceUrl() {
List<ServiceInstance> list = discoveryClient.getInstances("STORES");
if (list != null && list.size() > 0 ) {
return list.get(0).getUri();
}
return null;
}

Consul Catalog 监视​

Consul Catalog Watch 利用了 Consul 的 服务监控 功能。Catalog Watch 会发起一个阻塞式的 Consul HTTP API 调用,以确定是否有任何服务发生变更。如果检测到新的服务数据,则会发布一个 Heartbeat 事件。

要更改配置监视的调用频率,请修改 spring.cloud.consul.config.discovery.catalog-services-watch-delay。默认值为 1000,单位为毫秒。此延迟指的是上一次调用结束到下一次调用开始之间的时间间隔。

要禁用目录监视,请设置 spring.cloud.consul.discovery.catalogServicesWatch.enabled=false。

该监视器使用 Spring 的 TaskScheduler 来调度对 Consul 的调用。默认情况下,它是一个 ThreadPoolTaskScheduler,其 poolSize 为 1。要更改 TaskScheduler,请创建一个类型为 TaskScheduler 的 Bean,并使用 ConsulDiscoveryClientConfiguration.CATALOG_WATCH_TASK_SCHEDULER_NAME 常量进行命名。