Spring Cloud 学习笔记 - Consul

Consul 介绍

Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其他工具(比如 ZooKeeper 等)。使用起来也较 为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。

Consul 优势

  • 使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接. 相比较而言, zookeeper 采用的是 Paxos, 而 etcd 使用的则是 Raft。
  • 支持多数据中心,内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟, 分片等情况等。 zookeeper 和 etcd 均不提供多数据中心功能的支持。
  • 支持健康检查。 etcd 不提供此功能。
  • 支持 http 和 dns 协议接口。 zookeeper 的集成较为复杂, etcd 只支持 http 协议。
  • 官方提供 web 管理界面, etcd 无此功能。

综合比较, Consul 作为服务注册和配置管理的新星, 比较值得关注和研究。

Consul 特性

  • 服务发现
  • 健康检查
  • Key/Value 存储
  • 多数据中心

Consul 角色

  • client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群。
  • server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其他数据中心通讯。 每个数据中心的 server 数量推荐为 3 个或是 5 个。

Consul 安装(Docker)

指定固定网络

创建自定义网络,并指定网段:172.10.0.0/16 并命名为mynetwork

1
docker network create --subnet=172.10.0.0/16 mynetwork

查看网络

1
docker network ls

创建dockerfile

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
32
33
34
35
36
37
FROM centos:7.4.1708
MAINTAINER kenny <wfgdlut@gmail.com>

#配置consul版本
ENV CONSUL_VERSION=1.5.2
ENV HASHICORP_RELEASES=https://releases.hashicorp.com

#创建用户
RUN groupadd consul && \
useradd -g consul consul

#安装consul 并放置到/usr/local/bin
RUN yum upgrade -y && \
yum install -y net-tools && \
yum install -y firewalld firewalld-config && \
yum install -y wget && \
yum install -y unzip && \
wget ${HASHICORP_RELEASES}/consul/${CONSUL_VERSION}/consul_${CONSUL_VERSION}_linux_amd64.zip && \
unzip consul_${CONSUL_VERSION}_linux_amd64.zip && \
rm -rf consul_${CONSUL_VERSION}_linux_amd64.zip && \
mv consul /usr/local/bin

#创建consul数据目录和配置目录
RUN mkdir -p /Users/kwang/consul/data && \
mkdir -p /Users/kwang/consul/config && \
chown -R consul:consul /Users/kwang/consul


#设置匿名卷 此目录数据不会保存到容器存储层
VOLUME /consul/data
VOLUME /consul/config

#开放端口
EXPOSE 8300
EXPOSE 8301 8301/udp 8302 8302/udp
EXPOSE 8500 8600 8600/udp
EXPOSE 80

编译Dockerfile

1
docker build -t consul:1.5.2 .

检查编译后的docker镜像

1
docker images

使用docker镜像创建容器

1
2
3
4
5
docker run -itd --name test2 --network mynetwork -P --ip 172.10.0.2 --rm consul:1.5.2
docker run -itd --name test3 --network mynetwork --ip 172.10.0.3 --rm consul:1.5.2
docker run -itd --name test4 --network mynetwork --ip 172.10.0.4 --rm consul:1.5.2
docker run -itd --name test5 --network mynetwork --ip 172.10.0.5 --rm consul:1.5.2
docker run -itd --name test6 --network mynetwork --ip 172.10.0.6 --rm consul:1.5.2

查看结果

1
docker ps -a

配置consul集群

这里我们先来配置server。从高可用上,最少要配置3个server。

进入每个容器,执行server启动命令(只有第一个主server需要指定bootstrap-expect参数):

1
2
3
docker exec -it test2 bash

consul agent -server -ui -node=server2 -bootstrap-expect=4 -bind=172.10.0.2 -data-dir /consul/data -config-dir /consul/config -join=172.10.0.2 -client 0.0.0.0

1
2
3
docker exec -it test3 bash

consul agent -server -node server3 -bootstrap-expect=4 -bind=172.10.0.3 -data-dir=/consul/data -config-dir=/consul/config -join=172.10.0.2
1
2
3
docker exec -it test4 bash

consul agent -server -node server4 -bootstrap-expect=4 -bind=172.10.0.4 -data-dir=/consul/data -config-dir=/consul/config -join=172.10.0.2
1
2
3
docker exec -it test5 bash

consul agent -server -node server5 -bootstrap-expect=4 -bind=172.10.0.5 -data-dir=/consul/data -config-dir=/consul/config -join=172.10.0.2
1
2
3
docker exec -it test6 bash

consul agent -server -node server6 -bootstrap-expect=4 -bind=172.10.0.6 -data-dir=/consul/data -config-dir=/consul/config -join=172.10.0.2

查看docker test2的8500端口自动分配的本地对应端口

访问 http://localhost:32769,可以看到consul server已成功启动,并组成了集群。

加入几个client节点

1
2
3
docker run -itd --name test7 --network mynetwork --ip 172.10.0.7 --rm consul:1.5.2 &&
docker run -itd --name test8 --network mynetwork --ip 172.10.0.8 --rm consul:1.5.2 &&
docker run -itd --name test9 --network mynetwork --ip 172.10.0.9 --rm consul:1.5.2
1
2
docker exec -it test7 bash
consul agent -node node1 -bind=172.10.0.7 -data-dir=/consul/data -config-dir=/consul/config -join=172.10.0.2
1
2
docker exec -it test8 bash
consul agent -node node2 -bind=172.10.0.8 -data-dir=/consul/data -config-dir=/consul/config -join=172.10.0.2
1
2
docker exec -it test9 bash
consul agent -node node3 -bind=172.10.0.9 -data-dir=/consul/data -config-dir=/consul/config -join=172.10.0.2

查看node节点

http://localhost:32769/ui/dc1/nodes

Spring Cloud 集成 Consul

新建服务端项目(Spring Cloud)

pom.xml

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mindex</groupId>
<artifactId>consul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>consul</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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-test</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</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.properties

1
2
3
4
5
spring.application.name=spring-cloud-consul-test
spring.cloud.consul.host=localhost
spring.cloud.consul.port=32769
#注册到consul的服务名称
spring.cloud.consul.discovery.serviceName=service-test

启动类 ConsulApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.mindex.consul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class ConsulApplication {

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

}

测试类 ConsulController.java

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
32
33
34
35
36
37
38
39
40
package com.mindex.consul.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.InetAddress;
import java.net.UnknownHostException;

@RestController
@Slf4j
public class ConsulController {

@GetMapping("/ip")
public String getIpAddress() {
InetAddress address = null;

try {
address = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}

return "This is a service from IP: " + address.getHostAddress();
}


@RequestMapping("/hello")
public String hello() {
InetAddress address = null;

try {
address = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return "hello consul, IP: " + address.getHostAddress();
}
}

发布应用

通过不同的端口-Dserver.port将同一个服务发布到不同的端口上

新建Consumer端项目(Spring Cloud)

pom.xml

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mindex.consul</groupId>
<artifactId>consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>consumer</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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-test</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</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.properties

1
2
3
4
5
spring.application.name=spring-cloud-consul-consumer
spring.cloud.consul.host=localhost
spring.cloud.consul.port=32769
#设置不需要注册到 consul 中
spring.cloud.consul.discovery.register=false

服务测试类 ServiceController.java

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
32
33
34
35
36
37
package com.mindex.consul.consumer.controller;

import lombok.extern.slf4j.Slf4j;
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;

@RestController
@Slf4j
public class ServiceController {

@Autowired
private LoadBalancerClient loadBalancer;

@Autowired
private DiscoveryClient discoveryClient;

//获取所有服务
@RequestMapping("/services")
public Object services() {
return discoveryClient.getInstances("service-test");
}

@RequestMapping("/hello")
public String call() {
ServiceInstance serviceInstance = loadBalancer.choose("service-test");
System.out.println("服务地址:" + serviceInstance.getUri());
System.out.println("服务名称:" + serviceInstance.getServiceId());
String callServiceResult = new RestTemplate().getForObject(serviceInstance.getUri().toString() + "/hello", String.class);
log.info("callServiceResult = {}:{}", callServiceResult,serviceInstance.getPort());
return callServiceResult;
}
}

检验结果

列出所有服务

http://localhost:8503/services

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
[
{
"instanceId": "spring-cloud-consul-test-8501",
"serviceId": "service-test",
"host": "192.168.0.103",
"port": 8501,
"secure": false,
"metadata": {
"secure": "false"
},
"uri": "http://192.168.0.103:8501",
"scheme": null
},
{
"instanceId": "spring-cloud-consul-test-8502",
"serviceId": "service-test",
"host": "192.168.0.103",
"port": 8502,
"secure": false,
"metadata": {
"secure": "false"
},
"uri": "http://192.168.0.103:8502",
"scheme": null
}
]

调用特定服务

http://localhost:8503/hello

hello consul, IP: 192.168.0.103

查看日志,服务已自动负载均衡。

本文标题:Spring Cloud 学习笔记 - Consul

文章作者:王方钢 / Kenny Wang

发布时间:2019年07月17日 - 10:07

最后更新:2019年09月03日 - 20:09

原始链接:https://wangfanggang.com/Java/consul/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

王方钢 / Kenny Wang wechat
0%