# Consul

## 搭建Consul服务

### 搭建单节点Consul

1. 下载consul二进制文件：https://www.consul.io/downloads
2. 放置到path路径下

使用如下命令启动consul：

```shell
nohup consul  agent -server -data-dir=/usr/local/consul-data/  -node=agent-one -bind=0.0.0.0 -bootstrap-expect=1 -client=0.0.0.0 -ui > /usr/local/consul-data/logs/consul.lo
g 2>&1 &
```

### 单节点扩容为集群

```shell
nohup consul agent -bind=0.0.0.0 -client=0.0.0.0 -data-dir=/usr/local/consul-data -node=agent-four -join=172.16.111.130 > /usr/local/consul-data/logs/consul.log 2>&1 &
```

> 172.16.111.130 为单点服务的 IP 地址

### 搭建Consul集群

```shell
# 10.200.110.90启动consul
consul agent -server -bootstrap-expect=3 -data-dir=/tmp/consul -node=10.200.110.90 -bind=10.200.110.90 -client=0.0.0.0 -datacenter=shenzhen -ui
# 10.200.110.91启动consul
consul agent -server -bootstrap-expect 3 -data-dir /tmp/consul -node 10.200.110.91 -bind=10.200.110.91 -client=0.0.0.0 -datacenter shenzhen -ui
# 10.200.110.93启动consul
consul agent -server -bootstrap-expect 3 -data-dir /tmp/consul -node 10.200.110.93 -bind=10.200.110.93 -client=0.0.0.0 -datacenter shenzhen -ui
```

### consul 命令参数

* `server`： 以server身份启动。默认是client
* `bootstrap-expect`：集群要求的最少server数量，当低于这个数量，集群即失效。
* `data-dir`：data存放的目录，更多信息请参阅consul数据同步机制
* `node`：节点id，集群中的每个node必须有一个唯一的名称。默认情况下，Consul使用机器的hostname
* `bind`：监听的ip地址。默认绑定0.0.0.0，可以不指定。表示Consul监听的地址,而且它必须能够被集群中的其他节点访问。Consul默认会监听第一个private IP,但最好还是提供一个。生产设备上的服务器通常有好几个网卡，所以指定一个不会出错
* `client`: 客户端的ip地址，0.0.0.0是指谁都可以访问（不加这个，下面的ui :8500无法访问）
* `ui`: 可以访问UI界面
* `-config-dir` 指定配置文件夹，Consul会加载其中的所有文件
* `-datacenter` 指定数据中心名称，默认是 dc1

## Spring Cloud 使用Consul

### 使用 Consul 作为注册中心提供服务

添加依赖：

```xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
```

服务提供方的Spring Boot配置：

```yaml
server:
  port: 8501
spring:
  application:
    name: producer
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        serviceName: producer
```

服务提供方的启动类：

```java
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulProducerApplication {

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

### 消费Consul 服务

添加依赖：

```xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
```

消费服务：

```java
@RestController
public class ServiceController {

    @Autowired
    private LoadBalancerClient loadBalancer;

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 获取名称为producer的服务
     */
    @RequestMapping("/services")
    public Object services() {
        return discoveryClient.getInstances("producer");
    }

    /**
     * 负载均衡(轮询)的方式选择一个名称为producer的服务
     */
    @RequestMapping("/discover")
    public Object discover() {
        return loadBalancer.choose("producer").getUri().toString();
    }

    @GetMapping("/invoke/hello")
    public String invokeHello() {
        String uri = loadBalancer.choose("producer").getUri() + "/hello";
        return restTemplate.getForObject(uri, String.class);
    }
}

```

### 使用Consul作为配置中心

添加依赖：

```xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
```

在consul添加配置：

![image-20220322140756986](https://2351062869-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7b2CdwBN9liniVJpfEAc%2Fuploads%2Fgit-blob-249f99d672041087911cde52326bca077333572c%2Fimage-20220322140756986.png?alt=media)

配置内容为：

```yaml
config:
  info: "张三"
```

目标服务的SpringBoot Yaml配置（注意是bootstrap.yaml）：

```yaml
server:
  port: 8501
spring:
  application:
    name: producer
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        serviceName: producer
      config:
        enabled: true #是否启用配置中心功能
        format: yaml #设置配置值的格式
        prefix: config #设置配置所在目录
        profile-separator: ':' #设置配置的分隔符
        data-key: data #配置key的名字，由于Consul是K/V存储，配置存储在对应K的V中
```

消费配置：

```java
@RestController
@RefreshScope  // 自动刷新配置
public class HelloController {

    // 对应在consul配置中心中的key为
    // 路径/服务名称/key
    // 这里是
    // /config/producer/data
    // 如果需要不同的profile区分，那么则应该是
    // /config/producer:dev/dta

    @Value("${config.info}")
    private String configInfo;

    @RequestMapping("/hello")
    public String hello() {
        return "hello " + configInfo;
    }
}
```

## Consul 的基本原理

![架构图](https://2351062869-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7b2CdwBN9liniVJpfEAc%2Fuploads%2Fgit-blob-dc6dd73792a71fadaf3d9b1051d9ac392e8cbc4a%2Fconsul-arch.png?alt=media)

上图共有两个datacenter 数据中心，也就是两个Consul集群。两个数据中心之间通过Internet网络（WAN）与 Gossip协议在端口8302上进行数据交互与同步。

每个datacenter内部，也就是每个集群是由多个consul agent组件构成，根据特性又可以将其分为：

1. Server：参与共识仲裁(raft)、存储群集状态(日志存储)、处理查询、维护与周边(LAN/WAN)各节点关系
2. Client：负责通过该节点注册到consul的微服务的健康检查、将客户端注册请求以及查询转化为对server的RPC请求、维护与周边(LAN/WAN)各节点关系
