Consul 是 HashiCorp 公司推出来的开源产品。主要提供了:服务发现、服务隔离、服务配置等功能。相比于 其他注册中心组件Eureka 和Zookeeper,Consul 配置更加一站式,因为它内置了很多微服务常见的功能:服务发现与注册、分布式一致性协议实现、健康检查、键值对存储、多数据中心等,我们不再需要借助第三方组件来实现这些功能。
Spring Cloud Consul 具有如下特性:
Spring Cloud Consul 中文文档:https://www.springcloud.cc/spring-cloud-consul.html
不同于 Eureka ,Consul 使用 Go 语言开发,所以我们需要先安装Consul 。
从官网直接下载软件,官网下载地址:https://www.consul.io/downloads.html。下载完成后只有一个exe文件,首先双击运行该文件;
在命令行中输入以下命令可以查看版本号:
consul --version
然后使用开发模式启动:
consul agent -dev
浏览器访问 http://localhost:8500 ,进入Consul首页。
在 Linux 中,首先执行如下命令下载 Consul:
wget https://releases.hashicorp.com/consul/1.6.2/consul_1.6.2_linux_amd64.zip
下载完成后解压压缩包
unzip consul_1.6.2_linux_amd64.zip
解压完成后,我们可以看到consul文件,然后执行以下命令启动consul:
./consul agent -dev -ui -node=consul-dev -client=192.168.91.128
参数说明:
其他参数:
注意:192.168.91.128应该为内网ip。还要确保8500端口可用
启动成功后,浏览器请求http://101.43.30.7:8500/ui/dc1/services,访问consul管理后台
集群部署Consul的相关命令:
./consul members:查看集群信息
./consul join consul服务IP:添加节点
./consul info:查看节点的具体信息
创建Spring Boot项目,作为服务提供者,添加以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
成功创建项目后,application.properties添加如下配置:
spring.application.name=consul-provider
server.port=2000
#consul的主机地址
spring.cloud.consul.host=101.43.30.7
#conusl的端口
spring.cloud.consul.port=8500
#服务的名称
spring.cloud.consul.discovery.service-name=consul-provider
spring.cloud.consul.discovery.heartbeat.enabled=true
在启动类上开启服务发现的功能:
@SpringBootApplication
//开启服务发现功能
@EnableDiscoveryClient
public class ConsulProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulProviderApplication.class, args);
}
}
启动项目,访问consul管理后台界面,可以看到已经注册成功。
(如果服务总是报红叉,则需要在application.yml配置文件中增加spring.cloud.consul.discovery.heartbeat.enabled=true,打开Consul的心跳机制)
创建HelloController添加接口/hello
@RestController
public class HelloController {
@Value("${server.port}")
Integer port;
@GetMapping("/hello")
public String hello(){
return "hello" + port;
}
}
对项目进行打包,命令行启动多一个provider实例:
java -jar consul-provider-0.0.1-SNAPSHOT.jar --server.port=6000
创建Spring Boot项目consul-consumer,作为服务消费者,添加以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
成功创建项目后,application.properties添加如下配置:
spring.application.name=consul-consumer
server.port=2002
#consul的主机地址
spring.cloud.consul.host=101.43.30.7
#conusl的端口
spring.cloud.consul.port=8500
#服务的名称
spring.cloud.consul.discovery.service-name=consul-consumer
在启动类上开启服务发现的功能,并注入RestTemplate:
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulConsumerApplication.class, args);
}
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
测试服务调用
@RestController
public class HelloController {
@Autowired
LoadBalancerClient loadBalancerClient;
@Autowired
RestTemplate restTemplate;
@GetMapping("/hello")
public void hello() {
ServiceInstance choose = loadBalancerClient.choose("consul-provider");
System.out.println("服务地址:" + choose.getUri());
System.out.println("服务名称:" + choose.getServiceId());
String s = restTemplate.getForObject(choose.getUri() + "/hello",
String.class);
System.out.println(s);
}
}
这里通过loadBalancerClient获取要调用的ServiceInstance,获取到调用地址之后再使用RestTemplate去调用。浏览器请求http://localhost:2002/hello ,查看控制台的请求结果。
我们还可以在consul中实现服务的统一配置,创建Maven模块consul-config,在pom.xml中添加如下依赖:
<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">
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.1.RELEASEversion>
<relativePath/>
parent>
<modelVersion>4.0.0modelVersion>
<groupId>top.javahaigroupId>
<artifactId>consul-configartifactId>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Hoxton.SR6spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
添加配置文件application.yml,配置启用dev环境:
spring:
profiles:
active: dev
添加配置文件bootstrap.yml,对Consul的配置功能进行配置:
server:
port: 9101
spring:
application:
name: consul-config
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: consul-config
#打开心跳机制
heartbeat:
enabled: true
config:
#是否启用配置中心功能
enabled: true
#设置配置值的格式
format: yaml
#设置配置所在目录
prefix: config
#设置配置的分隔符
profile-separator: ':'
#配置key的名字,由于Consul是K/V存储,配置存储在对应K的V中
data-key: data
创建ConfigController,提供接口/configInfo,从Consul配置中心中获取配置信息.使用@RefreshScope注解实现动态加载配置文件。
@RefreshScope刷新机制原理
@RestController
@RefreshScope
public class ConfigController {
@Value("${config.info:}")
private String configInfo;
@GetMapping("/configInfo")
public String getConfigInfo() {
return configInfo;
}
}
在Consul后台添加如下配置:
添加配置存储的key为:
config/consul-config:dev/data
添加配置存储的value为:
config:
info:"config info onsul-config for dev"
我们修改Consul中的配置信息,再次调用查看配置的接口,就会发现配置也刷新了。这是因为Consul使用其自带的Control Bus 实现了一种事件传递机制,从而实现了动态配置刷新功能。
CAP理论是分布式架构中重要理论。C(Consistency)一致性、A(Availablity)可用性、P(Partition Tolerance)分区容错性。分布式系统不可能同时满足三个特性,最多只能满足其中两个特性。根据实际情况选择不同的特性组合对应的技术栈。
一致性:对于客户端的每次读操作,要么读到的是最新的数据,要么读取失败。换句话说,一致性是站在分布式系统的角度,对访问本系统的客户端的一种承诺:要么我给您返回一个错误,要么我给你返回绝对一致的最新数据,不难看出,其强调的是数据正确。
可用性:任何客户端的请求都能得到响应数据,不会出现响应错误。换句话说,可用性是站在分布式系统的角度,对访问本系统的客户的另一种承诺:我一定会给您返回数据,不会给你返回错误,但不保证数据最新,强调的是不出错。
分区容忍性:由于分布式系统通过网络进行通信,网络是不可靠的。当任意数量的消息丢失或延迟到达时,系统仍会继续提供服务,不会挂掉。换句话说,分区容忍性是站在分布式系统的角度,对访问本系统的客户端的再一种承诺:我会一直运行,不管我的内部出现何种数据同步问题,强调的是不挂掉。
Consul 遵循CAP原理中的CP原则,保证了强一致性和分区容错性,且使用的是Raft算法,比Zookeeper使用的Paxos算法更加简单。虽然保证了强一致性,但是可用性就相应下降了,例如服务注册的时间会稍长一些,因为 Consul 的 raft 协议要求必须过半数的节点都写入成功才认为注册成功 ;在leader挂掉了之后,重新选举出leader之前会导致Consul 服务不可用。Consul 保证了强一致性但牺牲了可用性。
Eureka:主要支持的是AP,强调高可用和分区容错;
Consul和Zookeeper:主要支持CP,强调数据一致性和分区容错。
Eureka拥有一个自我保护机制,当在90s内如果有过多的心跳包丢失,Eureka不会直接注销该服务,而是选择采用保护机制不去注销服务,等到90s到期如果仍然不能接受心跳包就直接注销,这种为了保证最大吞吐量而牺牲数据一致性的思想就是AP;
Zookeeper采用创建临时节点的机制来注册服务,由于是临时节点在连接断开时就会立刻被删除,对应着服务就被注销,这种思想对应的就是CP;
Consul在微服务器断开时会立刻停止服务,必须过半数的节点都写入成功才认为注册成功 ;在leader挂掉了之后,重新选举出leader之前会导致Consul 服务不可用的方式,来保证数据一致性原则。