1.1 简介
1.1.1 概述
Consul 是 HashiCorp 公司推出的开源工具,Consul 由 Go 语言开发,部署起来非常容易,只需要极少的可执行程序和配置文件,具有绿色、轻量级的特点。Consul 是分布式的、高可用的、 可横向扩展的用于实现分布式系统的服务发现与配置。这些功能中的每一项都可以根据需要单独使用,也可以一起使用来构建一个完整的服务网格。Consul 分为 Client 和 Server 两种节点(所有的节点也被称为 Agent),Server 节点保存数据,Client 负责健康检查及转发数据请求到 Server。Consul 的主要功能有:
♞ 服务发现:Consul 的客户端可以注册一个服务,比如 api 或 mysql,其他客户端可以使用 Consul 来发现特定服务的提供者。使用 DNS 或 HTTP,应用程序可以很容易地找到他们所依赖的服务。
♞ 健康检查:Consul 客户端可以提供任何数量的健康检查,要么与给定的服务相关联(如: “webserver是否返回 200 OK”),要么与本地节点相关联(如: “内存利用率是否低于 90%”)。这些信息可以运维人员用来监控集群的健康状况,并被服务发现组件来路由流量(比如: 仅路由到健康节点)
♞ KV存储:应用程序可以利用 Consul 的层级 K/V 存储来实现任何目的,包括动态配置、功能标记、协调、领导者选举等。Consul 提供了 HTTP API,使其非常简单以用。
♞ 安全服务通信: Consul 可以为服务生成和分发 TLS( 传输层安全性协议) 证书,以建立相互的 TLS 连接。可以使用 Intention 来定义哪些服务被允许进行通信。服务隔离可以通过可以实时更改 Intention 策略轻松管理,而不是使用复杂的网络拓扑结构和静态防火墙规则。
♞ 多数据中心:Consul 支持开箱即用的多数据中心。这意味着 Consul 的用户不必担心建立额外的抽象层来发展到多个区域。
1.1.2 安装
☞ Windows
♞ 首先去官网下载对应版本的 ☞ Consul
♞ Windows 中直接双击安装,黑框一闪而过就已经装好了
♞ 使用 consul --version
查看安装的版本
♞ 使用 sonsul agent -dev
启动开发模式
♞ 访问 http://localhost:8500
进入 consul web 界面
☞ Linux
♞ 使用 yum install -y unzip zip
命令在服务器上安装 zip 解压
♞ 将 consul 安装 包上传至服务器
♞ 使用 unzip xxx
解压
♞ 使用 ./consul agent -server -bootstrap-expect 1 -data-dir=/tmp/consul -node=consul_1 -bind=127.0.0.1 -client=0.0.0.0 -ui
启动 consul,其中 -server
表示是 server 模式;-bootstrap-expect=1
表示是集群中有 1 台服务器;-bootstrap
该模式 node 可以指定自己作为 leader ,如果是非 leader 可不加该参数;-data-dir=/tmp/consul
为代理存储状态提供了一个数据目录;-node=n2
该服务器节点名;-bind=127.0.0.1
节点绑定的 ip;-ui
非必须 webui 的路径,用 web 来管理 consul。☞ 其他配置详见
1.1.3 相关依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
1.2 Provider
1.2.1 配置文件
server:
port: 8093
spring:
application:
name: ProviderServer
cloud:
consul:
host: localhost
port: 8500
discovery:
# 表示注册时使用 ip 而不是 hostname
prefer-ip-address: true
# 健康检查失败多长时间取消注册
health-check-critical-timeout: 10s
1.2.2 启动类
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/10/29
* @description 服务提供者启动类
*/
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
1.2.3 服务类
/**
* Created with IntelliJ IDEA.
*
* @author gaohu9712@163.com
* @date 2020/10/29
* @description
*/
@RestController
@RequestMapping("/provider")
public class ProviderController {
@GetMapping("/get")
public Object get() {
return "你已经消费了";
}
}
1.2.4 启动服务
1.3 Consumer
1.3.1 配置文件
server:
port: 80
spring:
application:
name: ConsumerServer
cloud:
consul:
host: localhost
port: 8500
discovery:
# 表示注册时使用 ip 而不是 hostname
prefer-ip-address: true
# 健康检查失败多长时间取消注册
health-check-critical-timeout: 10s
1.3.2 启动类
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/10/29
* @description 消费者启动类
*/
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
1.3.3 服务类
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/10/29
* @description
*/
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/go")
public void go() {
List<ServiceInstance> providerServer = discoveryClient.getInstances("ProviderServer");
if (0 == providerServer.size()) {
return;
}
ServiceInstance serviceInstance = providerServer.get(0);
System.out.print(serviceInstance.getUri() + " --- ");
String url = serviceInstance.getUri() + "/provider/get";
RestTemplate restTemplate = new RestTemplate();
String str = restTemplate.getForObject(url, String.class);
System.out.println(str);
}
}
1.3.4 启动服务
1.3.5 请求服务
1.4 集群
1.4.1 Consul 集群
分别启动三台服务器上的 Consul,注意此时集群没有搭建完毕,访问 web 会报错
./consul agent -server -bootstrap-expect=3 -data-dir=/tmp/data -node=consul_1 -bind=192.168.91.131 -client=0.0.0.0 -ui
./consul agent -server -bootstrap-expect=3 -data-dir=/tmp/data -node=consul_2 -bind=192.168.91.134 -client=0.0.0.0
./consul agent -server -bootstrap-expect=3 -data-dir=/tmp/data -node=consul_3 -bind=192.168.91.135 -client=0.0.0.0
使用 ./consul join 192.168.91.131
命令将 2号、3号加入 1号中,如果我们的服务只有 10 个左右那么至此集群搭建可以就此完毕。
但是我们的服务有更多的话,就需要加入 client 端,正常使用 Consul 的时候还是要有 Client 才好,这也符合 Consul 的反熵设计。使用 ./consul agent -data-dir=/tmp/data -node=consul_client -bind=192.168.91.136 -client=0.0.0.0
启动 consul client 并将其加入 131 上的 1 号节点中中。此时我们一个完整的集群才算搭建完成。
1.4.2 Provider 集群
☞ 配置文件
spring:
application:
name: ProviderServer
cloud:
consul:
host: 192.168.91.136
port: 8500
discovery:
# 表示注册时使用 ip 而不是 hostname
prefer-ip-address: true
# 健康检查失败多长时间取消注册
health-check-critical-timeout: 10s
我们发现配置文件与单体时没有什么区别,那么是不是 consul 中服务注册到节点后,其他节点自动同步呢?在 web 控制台我们发现只有 client 节点有,而其他节点并没有显示。这是为什么呢?这个根本原因是服务发现的实现原理不同。
Consul 的数据同步也是强一致性的,服务的注册信息会在 Server 节点之间同步,服务的信息还是持久化保存的,即使服务部署不可用了,仍旧可以查询到这个服务部署。但是业务服务的可用状态是由注册到的 Agent 来维护的,Agent 如果不能正常工作了,则无法确定服务的真实状态。并且 Consul 是相当稳定了,Agent 挂掉的情况下大概率服务器的状态也可能是不好的,此时屏蔽掉此节点上的服务是合理的。
Consul 也确实是这样设计的,DNS 接口会自动屏蔽挂掉节点上的服务,HTTP API 也认为挂掉节点上的服务不是 passing 的。鉴于 Consul 健康检查的这种机制,同时避免单点故障,所有的业务服务应该部署多份,并注册到不同的 Consul 节点。
☞ 服务发现原理
首先需要有一个正常的 Consul 集群,有 Server,有 Leader。这里在服务器 Server 1、Server 2、Server 3 上分别部署了 Consul Server。假设他们选举了 Server 2 上的 Consul Server 节点为 Leader。这些服务器上最好只部署 Consul 程序,以尽量维护 Consul Server 的稳定。
然后在服务器 Server 4 和 Server 5 上通过 Consul Client 分别注册 Service A、B、C,这里每个 Service 分别部署在了两个服务器上,这样可以避免 Service 的单点问题。Consul Client 可以认为是无状态的,它将注册信息通过 RPC 转发到 Consul Server,服务信息保存在 Server 的各个节点中,并且通过 Raft 实现了强一致性。
最后在服务器 Server 6 中 Program D 需要访问 Service B,这时候 Program D 首先访问本机 Consul Client 提供的 HTTP API,本机 Client 会将请求转发到 Consul Server。Consul Server 查询到 Service B 当前的信息返回,最终 Program D 拿到了 Service B 的所有部署的 IP 和端口,然后就可以选择 Service B 的其中一个部署并向其发起请求了。
如果服务发现采用的是 DNS 方式,则 Program D 中直接使用 Service B 的服务发现域名,域名解析请求首先到达本机 DNS 代理,然后转发到本机 Consul Client,本机 Client 会将请求转发到 Consul Server。Consul Server 查询到 Service B 当前的信息返回,最终 Program D 拿到了 Service B 的某个部署的 IP 和端口。
1.4.3 测试
为了验证其他 consul 节点是可以获取到服务信息的,咱们将 consumer 注册到其他的 consul 节点,然后看使用能后实现远程调用。
我们可以发现确实可以获取到 provider 的相关信息并且实现远程调用,证明 consul 集群确实是将信息同步了,但是 consul 集群将服务交由注册方管理,当注册方失效时,通过其注册的服务全部失效。
☞ 源码
☞ Consul 中文文档
☞ 一篇文章了解Consul服务发现实现原理