Consul 多數據中心下的服務注冊發現與配置共享


1. Consul簡介

  Consul是HashiCorp公司推出的開源軟件,它提供了一套分布式高可用可橫向擴展的解決方案,能為微服務提供服務治理、健康檢查、配置共享等能力。
  Eurake2.x停止更新后,Spirng Cloud官網也推薦使用Consul實現服務注冊與發現。並且可以代替Sping Cloud Config實現配置中心。
  官網:https://www.consul.io/

2. 功能特點

  • 服務發現
      提供服務注冊和服務發現的功能,通過DNS和Http獲取服務信息。
  • 健康檢查
      通過健康檢查可以避免請求被路由到不健康的服務,並且主動斷開使其不再具備服務能力。
  • 鍵值存儲
      提供動態配置功能,通過長輪詢實現配置變更的即時通知。
  • 多數據中心
      通過簡單配置即可實現多數據中心,並且可以獲得其他數據中心的服務。
  • UI界面
      通過界面即可獲取服務信息、配置信息等其他信息。

3. CAP原則

  CAP原則指的是在分布式系統中Consistency(一致性)、Availability(可用性)、Partition Tolerance(分區容錯性)總是不能同時成立。
  一致性:它要求在任一時刻點分布式系統中的所有數據都處於同一狀態。
  可用性:在系統集群的一部分節點宕機后,系統依然能夠響應用戶的請求。
  分區容錯性:在網絡區間通信出現異常系統能容錯處理。
  一般來說,網絡總是存在不穩定性,分區容錯也總是存在的,所以默認CAP中的P總是成立的。
  一致性要求在任一時刻數據狀態一致,必然需要加鎖,因此不能保證可用性,反之同理。因此一致性和可用性總是互斥的。

4. 常見服務發現與注冊方案對比

功能 Eureka Consul Zookeeper Nacos
CAP AP CP CP CP+AP
一致性算法 Raft Paxos Raft
訪問協議 Http Http/DNS TCP Http/DNS
健康檢查 可配支持 支持 支持 支持
多數據中心 x 支持 x 支持
鍵值存儲 支持 支持 支持 支持
雪崩保護 支持 x x 支持
自動注銷實例 支持 x 支持 支持
監聽支持 支持 支持 支持 支持
安全性 x ACL/Https ACL ACL/Https
SpringCloud集成 支持 支持 支持 支持
Dubbo集成 x x 支持 支持
K8S集成 x 支持 x 支持

  Consul采用Raft算法來保證數據的強一致性,雖然優勢明顯,但是也犧牲了一些特性:
  (1) 服務注冊比Eureka慢。因為Consul要求必須節點數過半才認為注冊成功;
  (2) Leader掛掉后,在重新選舉期間,Consul將不可用,為了一致性犧牲可用性。

5. Consul架構


  圖中有兩個數據中心:DATACENTER1、DATACENTER2,每個數據中心都有3個Server節點,該數量使得在故障轉移和性能之間達到平衡。

6. Consul端口說明

端口 說明
TCP/8300 用於服務器節點。客戶端通過該端口 RPC 協議調用服務端節點。
TCP/UDP/8301 用於單個數據中心所有節點之間的互相通信,即對 LAN 池信息的同步。它使得整個數據中心能夠自動發現服務器地址,分布式檢測節點故障,事件廣播(如Leader選舉事件)。
TCP/UDP/8302 用於單個或多個數據中心之間的服務器節點的信息同步,即對 WAN 池信息的同步。它針對互聯網的網絡高延遲進行了優化,能夠實現跨數據中心請求。
8500 基於 HTTP 協議,用於 API 接口或 WEB UI 訪問。
8600 作為 DNS 服務器,通過節點名查詢節點信息。

7. Spring Cloud Consul實現服務注冊發現

創建生產者1:

  • 創建Maven項目
  • 修改pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<artifactId>spring-cloud-consul-producer1</artifactId>
	<name>spring-cloud-consul-producer1</name>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.1.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-discovery</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
  • 創建Controller
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 業務Controller
 * 
 * @author CL
 *
 */
@RestController
public class BookController {

	/**
	 * 業務接口
	 * 
	 * @return
	 */
	@RequestMapping(value = "/get")
	public String get() {
		return "Consul-1";
	}

}
  • 創建啟動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 啟動類
 * 
 * @author CL
 *
 */
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulProducerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConsulProducerApplication.class, args);
	}

}
  • 創建application.yml
server:
  port: 9001

spring:
  application:
    name: spring-cloud-consul-producer
  cloud:
    consul:
      discovery:
        serviceName: consul-producer
      host: 127.0.0.1
      port: 8500

創建生產者2:

  • 創建Maven項目
  • 修改pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<artifactId>spring-cloud-consul-producer2</artifactId>
	<name>spring-cloud-consul-producer2</name>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.1.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-discovery</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>
  • 創建Controller
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 業務Controller
 * 
 * @author CL
 *
 */
@RestController
public class BookController {

	/**
	 * 業務接口
	 * 
	 * @return
	 */
	@RequestMapping(value = "/get")
	public String get() {
		return "Consul-2";
	}

}
  • 創建啟動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 啟動類
 * 
 * @author CL
 *
 */
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulProducerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConsulProducerApplication.class, args);
	}

}
  • 創建application.yml
server:
  port: 9002

spring:
  application:
    name: spring-cloud-consul-producer
  cloud:
    consul:
      discovery:
        serviceName: consul-producer
      host: 127.0.0.1
      port: 8500

創建消費者:

  • 創建Maven項目
  • 修改pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<artifactId>spring-cloud-consul-consumer</artifactId>
	<name>spring-cloud-consul-consumer</name>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.1.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-discovery</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
  • 創建Controller
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * 服務Controller
 * 
 * @author CL
 *
 */
@RestController
public class ServicesController {

	@Autowired
	private DiscoveryClient discoveryClient;

	@Autowired
	private LoadBalancerClient loadBalancerClient;

	/**
	 * 獲得所以服務列表
	 * 
	 * @return
	 */
	@RequestMapping(value = "/getAllServices")
	public List<ServiceInstance> getAllServices() {
		return discoveryClient.getInstances("consul-producer");
	}

	/**
	 * 獲取服務Uri
	 * 
	 * @return
	 */
	@RequestMapping("/getServiceUri")
	public String getServiceUri() {
		return loadBalancerClient.choose("consul-producer").getUri().toString();
	}

	/**
	 * 負載均衡調用提供者接口
	 * 
	 * @return
	 */
	@RequestMapping("/loadBalancer")
	public String call() {
		ServiceInstance serviceInstance = loadBalancerClient.choose("consul-producer");
		return new RestTemplate().getForObject(serviceInstance.getUri().toString() + "/get", String.class);
	}
}
  • 創建啟動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 啟動類
 * 
 * @author CL
 *
 */
@SpringBootApplication
public class ConsulConsumerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConsulConsumerApplication.class, args);
	}

}
  • 創建application.yml
server:
  port: 9003

spring:
  application:
    name: spring-cloud-consul-consumer
  cloud:
    consul:
      discovery:
        register: false
      host: 127.0.0.1
      port: 8500

部署Consul:
  Consul安裝部署(Windows單機、Docker集群)
啟動項目:
  啟動順序:生產者1 -> 生產者2 -> 消費者
  訪問Consul WEB UI:http://127.0.0.1:8500


測試:

  • 獲取全部服務信息
curl http://localhost:9003/getAllServices

#返回:

[
    {
        "instanceId": "spring-cloud-consul-producer-9001",
        "serviceId": "consul-producer",
        "host": "192.168.0.100",
        "port": 9001,
        "secure": false,
        "metadata": {
            "secure": "false"
        },
        "uri": "http://192.168.0.100:9001",
        "scheme": null
    },
    {
        "instanceId": "spring-cloud-consul-producer-9002",
        "serviceId": "consul-producer",
        "host": "192.168.0.100",
        "port": 9002,
        "secure": false,
        "metadata": {
            "secure": "false"
        },
        "uri": "http://192.168.0.100:9002",
        "scheme": null
    }
]
  • 獲取服務Uri(負載均衡)
#第一次調用:
curl http://localhost:9003/getServiceUri

#返回:

http://192.168.0.100:9001

#第二次調用:
curl http://localhost:9003/getServiceUri

#返回:

http://192.168.0.100:9002
  • 調用提供者接口(負載均衡)
#第一次調用:
curl http://localhost:9003/loadBalancer

#返回:

Consul-1

#第二次調用:
curl http://localhost:9003/loadBalancer

#返回:

Consul-2

8. Spring Cloud Consul實配置中心

  • 創建Maven項目
  • 修改pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<artifactId>spring-cloud-consul-config</artifactId>
	<name>spring-cloud-consul-config</name>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.1.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-discovery</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
  • 創建application.yml
server:
  port: 9004
  • 創建bootstrap.yml
      作為配置中心的相關配置必須寫到bootstrap.yml
spring:
  application:
    name: spring-cloud-consul-config
  cloud:
    consul:
      discovery:
        service-name: consul-config
      host: 127.0.0.1
      port: 8500
      config:
        # 是否啟用配置中心功能
        enabled: true
        # 設置配置所在目錄
        prefix: config
        # 設置應用程序使用的文件夾名稱,默認application
        defaultContext: application
        # 配置key的名字,由於Consul是K/V存儲,配置存儲在對應K的V中,配置文件路徑為:/config/application/key
        data-key: data
        # 設置配置值的格式,四種配置:YAML PROPERTIES KEY-VALUE FILES
        format: YAML
        # 設置配置的分隔符
        profile-separator: ','
  • 創建Controller
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 配置Controller
 * 
 * @author CL
 *
 */
@RestController
@RefreshScope
public class ConfigController {

	@Value("${project-name}")
	private String projectName;

	@RequestMapping(value = "/getInfo")
	public String getInfo() {
		return projectName;
	}

}
  • 創建啟動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 啟動類
 * 
 * @author CL
 *
 */
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulConfigApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConsulConfigApplication.class, args);
	}

}
  • 創建配置
      在Consul WEB UI的Key/Value下創建配置:
  • 啟動項目
  • 查看Consul WEB UI
  • 測試
curl http://localhost:9004/getInfo

#返回:

spring-cloud-consul-demo

9. 項目地址

  spring-cloud-consul-demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM