使用 Consul 進行服務發現

服務發現是基於微服務架構的關鍵原則之一。嘗試手動配置每個客戶端或某種形式的約定可能非常困難且非常脆弱。Consul 透過 HTTP APIDNS 提供服務發現服務。Spring Cloud Consul 利用 HTTP API 進行服務註冊和發現。這並不妨礙非 Spring Cloud 應用程式利用 DNS 介面。Consul Agent 伺服器執行在一個叢集中,透過 Gossip 協議進行通訊,並使用 Raft 一致性協議

如何啟用

要啟用 Consul 服務發現,請使用 group 為 org.springframework.cloud、artifact id 為 spring-cloud-starter-consul-discovery 的 starter。有關如何使用當前 Spring Cloud Release Train 設定構建系統的詳細資訊,請參閱 Spring Cloud 專案頁面

向 Consul 註冊

當客戶端向 Consul 註冊時,它會提供關於自身的元資料,例如主機和埠、id、名稱和標籤。預設會建立一個 HTTP 健康檢查,Consul 每隔 10 秒訪問 /actuator/health 端點。如果健康檢查失敗,服務例項將被標記為 critical(嚴重)。

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 Context ID 和 ${server.port}

要停用 Consul Discovery Client,您可以將 spring.cloud.consul.discovery.enabled 設定為 false。當 spring.cloud.discovery.enabled 設定為 false 時,Consul Discovery Client 也會被停用。

要停用服務註冊,您可以將 spring.cloud.consul.discovery.register 設定為 false

將管理註冊為獨立服務

當透過設定 management.server.port 屬性將管理伺服器埠設定為與應用程式埠不同的值時,管理服務將註冊為與應用程式服務分離的獨立服務。例如

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

上述配置將註冊以下 2 個服務

  • 應用服務

ID: myApp
Name: myApp
  • 管理服務

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 個服務

  • 應用服務

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-* 屬性)。

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 健康檢查。

應用請求頭

可以將請求頭應用於健康檢查請求。例如,如果您嘗試註冊使用 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 健康檢查

可以使用 Consul TTL 檢查來替代預設配置的 HTTP 檢查。主要區別在於應用程式向 Consul Agent 傳送心跳訊號,而不是 Consul Agent 嚮應用程式傳送請求。

應用程式用於傳送 ping 的時間間隔也可以配置。"10s" 和 "1m" 分別表示 10 秒和 1 分鐘。預設是 30 秒。

此示例說明了上述內容(更多選項請參閱附錄頁中的 spring.cloud.consul.discovery.heartbeat.* 屬性)。

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

TTL 應用狀態

對於 Spring Boot Actuator 應用程式,狀態由其可用的健康端點決定。當健康端點不可用時(無論是被停用還是並非 Spring Boot Actuator 應用程式),它假定應用程式處於良好健康狀態。

查詢健康端點時,預設使用根 健康組。透過設定以下屬性可以使用不同的健康組

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
自定義 TTL 應用狀態

如果您想配置自己的應用狀態機制,只需實現 ApplicationStatusProvider 介面

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

並使其對應用上下文可用

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

Actuator 健康指示器

如果服務例項是 Spring Boot Actuator 應用程式,它可以提供以下 Actuator 健康指示器。

DiscoveryClientHealthIndicator

當 Consul 服務發現啟用時,會配置並使 DiscoverClientHealthIndicator 可用於 Actuator 健康端點。此處檢視配置選項

ConsulHealthIndicator

配置了一個指標來驗證 ConsulClient 的健康狀況。

預設情況下,它會檢索 Consul Leader 節點的狀態和所有已註冊的服務。在註冊服務眾多的部署中,每次健康檢查都檢索所有服務可能會帶來較高的成本。要跳過服務檢索,僅檢查 Leader 節點狀態,請設定 spring.cloud.consul.health-indicator.include-services-query=false

要停用該指標,請設定 management.health.consul.enabled=false

當應用程式在bootstrap 上下文模式下執行時(預設),此指示器會載入到 bootstrap 上下文中,而不會提供給 Actuator 健康端點。

元資料

Consul 支援服務的元資料。Spring Cloud 的 ServiceInstance 有一個 Map<String, String> metadata 欄位,它從服務的 meta 欄位填充。要填充 meta 欄位,請在 spring.cloud.consul.discovery.metadataspring.cloud.consul.discovery.management-metadata 屬性上設定值。

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

上述配置將使得服務的 meta 欄位包含 myfield→myvalueanotherfield→anothervale

生成的元資料

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 map。

使 Consul 例項 ID 唯一

預設情況下,Consul 例項註冊時使用的 ID 等於其 Spring 應用上下文 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}}}

使用此元資料,並且在 localhost 上部署了多個服務例項時,隨機值將在這裡生效,使例項唯一。在 Cloudfoundry 中,vcap.application.instance_id 會在 Spring Boot 應用程式中自動填充,因此不需要隨機值。

查詢服務

使用負載均衡器

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 Watch

Consul Catalog Watch 利用了 Consul 監聽服務的能力。Catalog Watch 會進行阻塞的 Consul HTTP API 呼叫,以確定是否有服務發生變化。如果有新的服務資料,就會發佈一個 Heartbeat 事件。

要更改呼叫 Catalog Watch 的頻率,請修改 spring.cloud.consul.discovery.catalog-services-watch-delay。預設值為 1000,單位是毫秒。延遲時間是指上次呼叫結束到下次呼叫開始之間的時間間隔。

要停用 Catalog Watch,請設定 spring.cloud.consul.discovery.catalogServicesWatch.enabled=false

該 Watch 使用 Spring 的 TaskScheduler 來排程對 Consul 的呼叫。預設是一個 ThreadPoolTaskScheduler,其 poolSize 為 1。要更改 TaskScheduler,請建立一個型別為 TaskScheduler 的 bean,並使用常量 ConsulDiscoveryClientConfiguration.CATALOG_WATCH_TASK_SCHEDULER_NAME 作為其名稱。