一、前言
Consul是一款由HashiCorp公司开源的,用于服务治理的软件,Spring Cloud Consul对其进行了封装。Consul具有如下特点:
- 服务注册:自动注册和取消注册服务实例的网络位置
- 运行状况检查:检测服务实例何时启动并运行
- 分布式配置:确保所有服务实例使用相同的配置
Consul agent有两种运行模式:Server和Client。这里的Server和Client只是Consul集群层面的区分,与搭建在Cluster之上 的应用服务无关。 以Server模式运行的Consul agent节点用于维护Consul集群的状态,官方建议每个Consul Cluster至少有3个或以上的运行在Server mode的Agent,Client节点不限。
二、安装Consul
2.1 下载与安装
Consul下载地址: https://www.consul.io/downloads.html ,本文选择Windows 64bit 版本进行演示,讲解以Windows操作为主。下载后进行解压,:
Windows下打开解压目录,命令行执行:
consul.exe
Linux下在解压目录执行:
./consul
![]()
可以看到Consul所包含的命令,使用consul [命令] --help可以查看某个命令的具体用法。
2.2 启动
执行下面这条命令来启动一个Consul agent:
Windows:
consul.exe agent -dev -client 127.0.0.1
Linux:
./consul agent -dev -client 192.168.140.215
-dev表示创建一个开发环境下的server节点,在该模式下不会有任何持久化操作,即不会有任何数据写入到磁盘,所以这个模式适合用于开发过程,而不适用于生产环境。-client 192.168.140.215表示运行客户端使用ip地址192.168.140.215(Linux环境IP地址)去访问。
启动后,默认的端口号为8500,访问 http://127.0.0.1:8500
![]()
当前就一个consul服务。接下来我们开始创建服务提供者和服务消费者。
三、Server-Provider
创建一个Spring Boot项目
3.1 依赖引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </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>
|
3.2 application.yml改造
然后在配置文件里添加如下配置:
1 2 3 4 5 6 7 8 9 10 11 12
| spring: application: name: server-provider cloud: consul: host: 127.0.0.1 port: 8500 discovery: health-check-interval: 10s service-name: ${spring.application.name} register-health-check: true health-check-path: /check
|
spring.cloud.consul.host和spring.cloud.consul.port配置了consul的ip和端口;
spring.cloud.consul.discovery.service-name配置了该服务在consul里注册的服务名称;
spring.cloud.consul.discovery.register-health-check用于开启健康检查;
spring.cloud.consul.discovery.health-check-interval配置了健康检查的周期为10秒;
spring.cloud.consul.discovery.health-check-path配置了健康检查路径。
3.3 创建测试controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @RestController @Slf4j public class TestController {
@GetMapping("check") private String check() { log.info("health check"); return "ok"; }
@GetMapping("hello") public String hello() { log.info("hello"); return "hello from server provider"; } }
|
check方法用于监控检查,TestController还提供了一个hello方法,以供后续服务消费者调用。
spring.cloud.consul.discovery.health-check-path的默认值为/actuator/health,如果采用该默认值的话,还需要导入spring-boot-starter-actuator依赖。
3.4 开启服务注册与发行
最后,要开启服务注册与发行,需要在Spring Boot入口类上添加@EnableDiscoveryClient注解:
1 2 3 4 5 6 7 8 9
| @EnableDiscoveryClient @SpringBootApplication public class ServerProviderApplication {
public static void main(String[] args) { SpringApplication.run(ServerProviderApplication.class, args); }
}
|
3.5 启动测试
准备完毕后,然后启动两个实例,端口号分别为8101和8102,启动后,再次访问consul管理界面:
启动参数分别为:
server.port=8101
server.port=8102
![]()
![]()
服务提供者注册成功,接下来开始搭建服务消费者。
四、Server-Consumer
创建一个Spring Boot项目
4.1 依赖引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </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>
|
引入spring-boot-starter-actuator用于默认的健康检查。
4.2 配置application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13
| server: port: 8105
spring: application: name: server-consumer
cloud: consul: host: 127.0.0.1 port: 8500 discovery: service-name: ${spring.application.name}
|
4.3 创建测试controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| @RestController @Slf4j public class TestController {
@Autowired private DiscoveryClient discoveryClient; @Autowired private LoadBalancerClient loadBalancerClient;
private final RestTemplate restTemplate = new RestTemplate();
private static final String SERVER_ID = "server-provider";
@GetMapping("uri") public List<URI> getServerUris() { return this.discoveryClient.getInstances(SERVER_ID) .stream() .map(ServiceInstance::getUri).collect(Collectors.toList()); }
@GetMapping("hello") public String hello() { ServiceInstance instance = loadBalancerClient.choose(SERVER_ID); String url = instance.getUri().toString() + "/hello"; log.info("remote server url:{}", url); return restTemplate.getForObject(url, String.class); } }
|
SERVER_ID的值为服务提供者在consul注册中心的实例名称,即server-provider。通过DiscoveryClient我们可以获取到所有名称为server-provider的服务实例信息。通过LoadBalancerClient我们可以实现负载均衡地去获取服务实例,并通过RestTemplate去调用服务。
4.4 开启服务注册与发行
同样的,需要开启服务注册与发现需要在入口类上添加@EnableDiscoveryClient注解。
1 2 3 4 5 6 7 8 9
| @EnableDiscoveryClient @SpringBootApplication public class ServerConsumerApplication {
public static void main(String[] args) { SpringApplication.run(ServerConsumerApplication.class, args); }
}
|
4.5 启动测试
启动项目,然后查看consul控制台:
![]()
访问: http://127.0.0.1:8105/uri :
![]()
多次请求 http://127.0.0.1:8105/hello 。
![]()
![]()
服务调用是均衡的。
除此之外,consul内置了Ribbon,所以我们还可以通过@LoadBalanced标注的RestTemplate来实现负载均衡服务调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Autowired private RestTemplate restTemplate;
private static final String SERVER_ID = "server-provider";
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
@GetMapping("hello") public String hello() { String url = "http://" + SERVER_ID + "/hello"; return restTemplate.getForObject(url, String.class); }
|
效果是一样的。
五、consul集群
上面我们只是以-dev模式开启了一个单节点consul agent,生产环境下需要搭建consul集群来确保高可用。
5.1 集群概况
搭建consul集群时常用的命令有:
| 命令 | 解释 | 示例 |
|---|
| agent | 运行一个consul agent | consul agent -dev |
| join | 将agent加入到consul集群 | consul join IP |
| members | 列出consul cluster集群中的members | consul members |
| leave | 将节点移除所在集群 | consul leave |
准备了启用三个端口,组成一个集群,配置如下:
| 序号 | 节点名称 | 端口 | 角色 |
|---|
| 1 | consul-server-8501 | 8501 | server & web ui |
| 2 | consul-server-8502 | 8502 | server |
| 3 | consul-server-8503 | 8503 | server |
5.2 基础配置
5.2.1 创建配置文件
在consul解压目录创建conf文件夹,在里面新建以下文件:
data8501.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| { "datacenter": "dc1", "data_dir": "./data/8501", "log_level": "INFO", "server": true, "node_name": "consul-server-8501", "ui": true, "bind_addr": "127.0.0.1", "client_addr": "127.0.0.1", "advertise_addr": "127.0.0.1", "bootstrap_expect": 3, "ports": { "http": 8501, "server": 8601, "serf_lan": 8602, "serf_wan": 8603, "dns": 8604, "grpc": 8605 } }
|
data8502.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| { "datacenter": "dc1", "data_dir": "./data/8502", "log_level": "INFO", "server": true, "node_name": "consul-server-8502", "ui": false, "bind_addr": "127.0.0.1", "client_addr": "127.0.0.1", "advertise_addr": "127.0.0.1", "bootstrap_expect": 3, "ports": { "http": 8502, "server": 8606, "serf_lan": 8607, "serf_wan": 8608, "dns": 8609, "grpc": 8610 } }
|
data8503.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| { "datacenter": "dc1", "data_dir": "./data/8503", "log_level": "INFO", "server": true, "node_name": "consul-server-8503", "ui": false, "bind_addr": "127.0.0.1", "client_addr": "127.0.0.1", "advertise_addr": "127.0.0.1", "bootstrap_expect": 3, "ports": { "http": 8503, "server": 8611, "serf_lan": 8612, "serf_wan": 8613, "dns": 8614, "grpc": 8615 } }
|
解释一下上面这条命令的含义:
server表示以服务的形式启动agent
bind_addr表示绑定到本地的ip(有些服务器会绑定多块网卡,可以通过bind参数强制指定绑定的ip)
client指定客户端访问的ip(consul有丰富的api接口,这里的客户端指浏览器或调用方),0.0.0.0表示不限客户端ip
bootstrap-expect=3表示server集群最低节点数为3,低于这个值将工作不正常(注:类似zookeeper一样,通常集群数为奇数,方便选举,consul采用的是raft算法)
data-dir表示指定数据的存放目录(该目录必须存在)
node表示节点的名称
5.2.2 创建服务存储
在consul解压目录创建data文件夹,在里面新建文件夹:8501、8502、8503
5.2.3 创建启动脚本
在consul解压目录创建以下启动脚本:
consul-server-8501.bat
1
| consul.exe agent -config-file=.\conf\data8501.json
|
consul-server-8502.bat
1
| consul.exe agent -config-file=.\conf\data8502.json -retry-join=127.0.0.1:8602
|
consul-server-8503.bat
1
| consul.exe agent -config-file=.\conf\data8503.json -retry-join=127.0.0.1:8602
|
5.3 启动测试
依次启动上面三个服务,双击consul-server-8501.bat、consul-server-8502.bat、consul-server-8503.bat。
然后访问 http://localhost:8501
![]()
这个时候,我们将上面Server-Provider、Server-Consumer项目中consul的端口改到8503
1 2 3 4 5
| spring: cloud: consul: host: 127.0.0.1 port: 8503
|
重启后,请求访问 http://127.0.0.1:8105/hello
![]()
然后关闭掉consul-server-8503
![]()
再次请求访问 http://127.0.0.1:8105/hello
![]()
服务依旧获取成功。
可见,虽然我们在application.yml中配置consul的地址是127.0.0.1:8503,但由于我们构建的是consul集群,所以微服务启动时会获取到整个集群信息,即使consul-server-8503这个节点挂了,微服务可以从别的consul节点上获取注册的服务信息。
更多资料参考:
https://learn.hashicorp.com/tutorials/consul/get-started-install
https://learn.hashicorp.com/tutorials/consul/get-started-install