Service Discovery with Consul

服务发现是基于微服务架构的关键原则之一。尝试手工配置每个客户端或某种形式的约定是非常困难且容易出现的。Consul通过 HTTP APIDNS提供服务发现服务。Spring Cloud Consul利用HTTP API进行服务注册和发现。这并不会阻止非Spring Cloud应用程序利用DNS界面。Consul Agent服务器在一个 cluster中运行,该服务器通过一个 gossip protocol来通信并使用 Raft consensus protocol

How to activate

如需激活Consul服务发现,请使用组为`org.springframework.cloud`且工件ID为`spring-cloud-starter-consul-discovery`的starter。请参阅 Spring Cloud Project page了解有关使用当前Spring Cloud Release Train设置构建系统 的详细信息。

Registering with Consul

当一个客户端在Consul中注册时,它会提供有关其自己的元数据,如主机和端口、ID、名称和标签。默认创建一个 HTTP Check,该 HTTP Check使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 以外,则需要配置以找到该客户端。示例:

application.yml
spring:
  cloud:
    consul:
      host: localhost
      port: 8500

如果你使用 Spring Cloud Consul Config,并且设置了 spring.cloud.bootstrap.enabled=truespring.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

Registering Management as a Separate Service

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

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

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

  • Application Service:

ID: myApp
Name: myApp
  • Management Service:

ID: myApp-management
Name: myApp-management

管理服务将从应用程序服务处继承其 instanceIdserviceName。例如:

application.yml
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
  • Management Service:

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 Health Check

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-* 属性: link:appendix.html)。

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

您可以通过设置 spring.cloud.consul.discovery.register-health-check=false 完全禁用 HTTP 运行状况检查。

Applying Headers

可以将标头应用于运行状况检查请求。例如,如果您尝试注册一个使用 Vault BackendSpring Cloud Config服务器:

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

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

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

TTL Health Check

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

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

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

application.yml
spring:
  cloud:
    consul:
      discovery:
        heartbeat:
          enabled: true
          ttl: 10s

TTL Application Status

对于 Spring Boot Actuator 应用程序,状态由其可用的运行状况终结点决定。当运行状况终结点不可用时(禁用或不是 Spring Boot Actuator 应用程序),它会假定应用程序处于运行状况良好。

在查询运行状况端点时,默认使用根 health group。可以通过设置以下属性来使用不同的运行状况组:

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

您可以通过设置以下属性来完全禁用运行状况终结点:

application.yml
spring:
  cloud:
    consul:
      discovery:
        heartbeat:
          use-actuator-health: false
Custom TTL Application Status

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

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

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

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

Actuator Health Indicator(s)

如果服务实例是 Spring Boot Actuator 应用程序,可以为它提供以下 Actuator 健康指标。

DiscoveryClientHealthIndicator

当Consul服务发现处于活动状态时,将配置一个 DiscoverClientHealthIndicator供执行器健康端点使用。请参阅 here以了解配置选项。

ConsulHealthIndicator

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

默认情况下,它会检索 Consul 领导节点状态和所有已注册服务。在拥有许多注册服务的部署中,每次执行健康检查时检索所有服务都可能很耗时。要跳过服务检索并仅检查领导节点状态,请将 spring.cloud.consul.health-indicator.include-services-query=false 设为 false。

要禁用指标,请将 management.health.consul.enabled=false 设为 false。

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

Metadata

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

application.yml
spring:
  cloud:
    consul:
      discovery:
        metadata:
          myfield: myvalue
          anotherfield: anothervalue

上述配置会产生一个元字段包含 myfield→myvalueanotherfield→anothervalue 的服务。

Generated Metadata

Consul 自动注册会自动生成几个条目。

Table 1. Auto Generated Metadata
Key Value

'group'

属性 spring.cloud.consul.discovery.instance-group。只有当 instance-group 不为空时才会生成此值。

'secure'

如果属性 spring.cloud.consul.discovery.scheme 等于 'https',则为真,否则为假。

属性 spring.cloud.consul.discovery.default-zone-metadata-name,默认为 '区域'

属性 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 映射。

Making the Consul Instance ID Unique

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

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

有了此元数据,并在本地主机上部署了多个服务实例,那么随机值将发挥作用,让实例变得唯一。在 Cloudfoundry 中,vcap.application.instance_id 会在 Spring Boot 应用程序中自动填充,因此不需要随机值。

Looking up services

Using Load-balancer

Spring Cloud支持 Feign(REST 客户端生成器)和 Spring RestTemplate,用于使用逻辑服务名称/ID查找服务,而不是物理URL。Feign和支持发现的RestTemplate都利用 Spring Cloud LoadBalancer进行客户端负载平衡。

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

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

并像下面这样使用它(请注意我们是如何使用来自 Consul 的商店服务名称/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 是商店服务所在的数据中心。

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

Using the 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 Watch

Consul Catalog Watch利用consul来 watch services的能力。Catalog Watch执行一个阻塞Consul HTTP API调用以确定是否已更改任何服务。如果出现新的服务数据,将发布一个心跳事件。

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

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

监视使用 Spring TaskScheduler 为调用 Consul 安排时间。默认情况下,它是一个拥有 1 个 poolSizeThreadPoolTaskScheduler。要更改 TaskScheduler,请创建一个名为 ConsulDiscoveryClientConfiguration.CATALOG_WATCH_TASK_SCHEDULER_NAME 常量的 TaskScheduler 类型 bean。