68. Consul 服务发现

服务发现是基于微服务的体系结构的关键原则之一。尝试手动配置每个 Client 端或某种形式的约定可能非常困难并且非常脆弱。Consul 通过HTTP APIDNS提供服务发现服务。 Spring Cloud Consul 利用 HTTP API 进行服务注册和发现。这不会阻止非 Spring Cloud 应用程序利用 DNS 接口。 Consul Agents 服务器在cluster中运行,该cluster通过gossip protocol进行通信并使用筏共识协议

68.1 如何激活

要激活 Consul Service Discovery,请使用带有org.springframework.cloud组和工件 ID spring-cloud-starter-consul-discovery的启动器。有关使用当前 Spring Cloud Release Train 设置构建系统的详细信息,请参见Spring Cloud Project 页面

68.2 向 Consul 注册

当 Client 端向 Consul 注册时,它将提供有关其自身的元数据,例如主机和端口,id,名称和标签。默认情况下会创建一个 HTTP Check,Consul 每 10 秒会命中一次/health端点。如果运行状况检查失败,则将该服务实例标记为关键。

ConsulClient 端示例:

@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 应用)。如果 ConsulClient 端位于localhost:8500以外的其他位置,则需要进行配置才能找到该 Client 端。例:

application.yml.

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

Warning

如果您使用Spring Cloud Consul 配置,则需要将上述值放在bootstrap.yml而不是application.yml中。

来自Environment的默认服务名称,实例 ID 和端口分别是${spring.application.name},Spring Context ID 和${server.port}

要禁用 Consul Discovery Client,可以将spring.cloud.consul.discovery.enabled设置为false

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

68.2.1 将 Management 注册为单独的服务

当 Management 服务器端口设置为与应用程序端口不同时,通过设置management.server.port属性,Management 服务将被注册为与应用程序服务不同的服务。例如:

application.yml.

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

以上配置将注册以下两项服务:

  • Application Service:
ID: myApp
Name: myApp
  • Management Service:
ID: myApp-management
Name: myApp-management

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}

以上配置将注册以下两项服务:

  • 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

68.3 HTTP 运行状况检查

Consul 实例的运行状况检查默认为“/health”,这是 Spring Boot Actuator 应用程序中有用端点的默认位置。即使您使用非默认上下文路径或 Servlet 路径(例如server.servletPath=/foo)或 Management 端点路径(例如management.server.servlet.context-path=/admin),也需要更改这些内容,即使对于 Actuator 应用程序也是如此。还可以配置 Consul 用于检查运行状况终结点的时间间隔。 “ 10s”和“ 1m”分别代表 10 秒和 1 分钟。例:

application.yml.

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

您可以通过设置management.health.consul.enabled=false禁用运行状况检查。

68.3.1 元数据和 Consul 标签

Consul 尚不支持有关服务的元数据。 Spring Cloud 的ServiceInstance有一个Map<String, String> metadata字段。 Spring Cloud Consul 使用 Consul 标签来近似元数据,直到 Consul 正式支持元数据。格式为key=value的标签将被拆分并分别用作Map键和值。没有等号=的标记将用作键和值。

application.yml.

spring:
  cloud:
    consul:
      discovery:
        tags: foo=bar, baz

上面的配置将生成带有foo→barbaz→baz的 Map。

68.3.2 使 Consul 实例 ID 唯一

默认情况下,Consul 实例的 ID 等于其 Spring Application Context ID。默认情况下,Spring Application Context ID 为${spring.application.name}:comma,separated,profiles:${server.port}。在大多数情况下,这将允许一项服务的多个实例在一台计算机上运行。如果需要进一步的唯一性,则可以使用 Spring_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}}}

有了此元数据,并且在 localhost 上部署了多个服务实例,随机值将在其中加入以使实例唯一。在 Cloudfoundry 中,vcap.application.instance_id将自动填充在 Spring Boot 应用程序中,因此将不需要随机值。

68.3.3 将 Headers 应用于健康检查请求

Headers 可以应用于健康检查请求。例如,如果您尝试注册使用Vault BackendSpring Cloud Config服务器:

application.yml.

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

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

application.yml.

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

68.4 查找服务

68.4.1 使用功能区

Spring Cloud 支持Feign(RESTClient 端构建器),也支持Spring RestTemplate使用逻辑服务名称/标识而不是物理 URL 查找服务。 Feign 和可发现发现的 RestTemplate 都使用Ribbon进行 Client 端负载平衡。

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

@LoadBalanced
@Bean
public RestTemplate loadbalancedRestTemplate() {
     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 服务所在的数据中心。

68.4.2 使用 DiscoveryClient

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

@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;
}

68.5Consul 目录手表

Consul 目录监视利用 Consulwatch services的能力。 Catalog Watch 进行了阻塞的 Consul HTTP API 调用,以确定是否有任何服务已更改。如果有新的服务数据,则会发布心跳事件。

要更改称为 Config Watch 的频率,请更改spring.cloud.consul.config.discovery.catalog-services-watch-delay。默认值为 1000,以毫秒为单位。延迟是上一次调用结束与下一次调用开始之间的时间量。

要禁用目录监视集spring.cloud.consul.discovery.catalogServicesWatch.enabled=false

手表使用 Spring TaskScheduler来安排对 Consul 的调用。缺省情况下,它是poolSize为 1 的ThreadPoolTaskScheduler。要更改TaskScheduler,请创建一个TaskScheduler类型的 Bean,该 Bean 以ConsulDiscoveryClientConfiguration.CATALOG_WATCH_TASK_SCHEDULER_NAME常量命名。