Eureka替换方案Consul(不推荐)

A12X
1 Eureka闭源的影响
1.1 Eureka闭源

在Eureka的GitHub上,宣布Eureka 2.x闭源。这意味着如果开发者继续使用2.x分支上现有工作repo的一部分发布的代码库和组件,则自负风险。

1.2 Eureka的替换方案

1.2.1 Zookeeper

Zookeeper是一个分布式的,开放源代码的分布式应用程序协调服务,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

1.2.2 Consul

Consul是近几年比较流行的服务发现工具。
Consul的三个主要应用场景:服务发现、服务隔离、服务配置。

1.2.3 Nacos

Nacos是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos致力于帮助您发现、配置和管理微服务。Nacos提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据以及流量管理。Nacos帮助您更敏捷和容易的构建、交付和管理微服务平台。Nacos是构建以“服务”为中心的现代应用架构(例如微服务范式、云原生范式)的服务基础设施。

2 Consul简介

2.1 概述

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

2.2 Consul的优势

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

2.3 Consul的特性

服务发现。
健康检查。
key/value存储。
多数据中心。

2.4 Consul和Eureka的区别

2.4.1 一致性

Consul:强一致性(CP):
服务注册相比Eureka会稍慢一些。因为Consul的Raft协议要求必须过半的节点都写入成功才认为注册成功。
Leader挂掉后,重新选举期间整个Consul不可用。保证了强一致性,但牺牲了可用性。
Eureka:高可用性和最终一致性(AP):
服务注册相对要快,因为不需要等注册信息复制到其他节点,也不保证注册信息是否复制成功。
当数据出现不一致的时候,虽然A,B上的注册信息不完全相同,但是每个Eureka节点依然能够正常对外提供服务,这会出现查询服务信息时如果请求A查不到,但请求B就能查到。如果保证了可用性但牺牲了一致性。

2.4.2 开发语言和使用

Eureka是Servlet程序,跑在Servlet容器中。
Consul是Go语言编写而言的,安装启动即可。

2.5 Consul的下载和安装

Consul不同于Eureka需要单独安装,访问官网可以下载Consul的最新版本,目前使用的是Consul 1.8.4。根据不同的操作系统类型选择不同的安装包,Consul支持所有主流操作系统。

2.5.1 在linux中下载Consul

从官网下载最新版本的consul服务

wget https://releases.hashicorp.com/consul/1.8.4/consul_1.8.4_linux_amd64.zip

使用unzip命令解压

unzip consul_1.8.4_linux_amd64.zip

将解压好的consul可执行命令赋值到/usr/local/bin目录下

cp consul /usr/local/bin

测试一下

consul
2.5.2 启动Consul
以开发者模式快速启动,-client指定客户端可以访问的IP地址
consul agent -dev -client=0.0.0.0
启动之后,返回http://localhost:8500,可以看到Consul的管理界面:

3 Consul的基本使用

Consul支持健康检查,并提供了HTTP和DNS调用的API接口来完成服务的注册,服务发现以及KV存储这些功能。本人在VMWear中的Linux的IP地址是192.168.32.100。
3.1 服务注册和发现

3.1.1 注册服务

通过postman发送PUT请求到http://192.168.32.100:8500/v1/catalog/register地址可以完成服务注册。
{
“Datacenter”: “dc1”,
“Node”: “node01”,
“Address”: “192.168.1.57”,
“Service”: {
“ID”: “mysql-01”,
“Service”: “mysql”,
“tags”: [
“master”,
“v1”
],
“Address”: “192.168.1.57”,
“Port”: 3306
}
}

3.1.2 服务查询

通过postman发送GET请求到http://192.168.32.100:8500/v1/catalog/services地址获取所有的服务列表。

通过postman发送GET请求到http://192.168.32.100:8500/v1/catalog/service/mysql获取具体的服务。

3.1.3 服务删除

通过postman发送PUT请求到http://192.168.32.100:8500/v1/catalog/deregister删除服务。

3.2 Consul的KV存储

可以参照Consul提供的KV存储的API完成基于Consul的数据存储。
含义请求路径请求方式查看keyv1/kv/:keyGET保存或更新v1/kv/:keyput删除/v1/kv/:keyDELETE

key值中可以带/,可以看做是不同的目录结构。
value的值经过了base64编码,获取到数据后需要经过base64解码才能获取到原始值。数据不能大于521kb。
不同的数据中心的kv存储系统是独立的,使用dc=?参数指定。

4 基于Consul的服务注册

4.1 案例目标

准备一个商品微服务和订单微服务。
将商品微服务注册到Consul中。
订单微服务从consul中拉取所有的服务列表。

4.2 案例准备

4.2.1 sql脚本
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;


– Table structure for department


DROP TABLE IF EXISTS department;
CREATE TABLE department (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;


– Records of department


INSERT INTO department VALUES (1, ‘开发部’);
INSERT INTO department VALUES (2, ‘运维部’);


– Table structure for employee


DROP TABLE IF EXISTS employee;
CREATE TABLE employee (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
gender varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;


– Records of employee


INSERT INTO employee VALUES (1, ‘zhangsan’, ‘男’);
INSERT INTO employee VALUES (2, ‘李四’, ‘女’);


– Table structure for tb_order


DROP TABLE IF EXISTS tb_order;
CREATE TABLE tb_order (
id bigint(11) NOT NULL AUTO_INCREMENT,
user_id int(11) NULL DEFAULT NULL COMMENT ‘用户id’,
product_id int(11) NULL DEFAULT NULL COMMENT ‘商品id’,
number int(11) NULL DEFAULT NULL COMMENT ‘数量’,
price decimal(10, 2) NULL DEFAULT NULL COMMENT ‘单价’,
amount decimal(10, 2) NULL DEFAULT NULL COMMENT ‘总额’,
product_name varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT ‘商品名’,
username varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT ‘用户名’,
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;


– Table structure for tb_product


DROP TABLE IF EXISTS tb_product;
CREATE TABLE tb_product (
id bigint(20) NOT NULL AUTO_INCREMENT,
caption varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
inventory varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
price decimal(19, 2) NULL DEFAULT NULL,
product_desc varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
product_name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
status int(11) NULL DEFAULT NULL,
PRIMARY KEY (id) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;


– Records of tb_product


INSERT INTO tb_product VALUES (1, ‘iPhone’, ‘1’, 5000.10, ‘苹果手机就是香’, ‘苹果哇’, 50);


– Table structure for tb_user


DROP TABLE IF EXISTS tb_user;
CREATE TABLE tb_user (
id bigint(11) NOT NULL AUTO_INCREMENT,
username varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT ‘用户名’,
password varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT ‘密码’,
age int(3) NULL DEFAULT NULL COMMENT ‘年龄’,
balance decimal(10, 2) NULL DEFAULT NULL COMMENT ‘余额’,
address varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT ‘地址’,
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
4.2.2 商品微服务

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

spring_cloud_demo
com.xuguoguo
1.0

4.0.0

<artifactId>product_serivce-consul9003</artifactId>

<dependencies>
    <!--    服务监控    -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

application.yml server: port: 9003 # 微服务的端口号

spring:
application:
name: service-product # 微服务的名称
datasource:
url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql

微服务info内容详细信息

info:
app.name: xxx
company.name: xxx
build.artifactId: zookeeper被什么替代了 zookeeper替代方案_eureka
build.version: zookeeper被什么替代了 zookeeper替代方案_zookeeper_02
Product.java
package com.xuguoguo.product.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = “tb_product”)
public class Product implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "product_name")
private String productName;

@Column(name = "status")
private Integer status;

@Column(name = "price")
private BigDecimal price;

@Column(name = "product_desc")
private String productDesc;

@Column(name = "caption")
private String caption;

@Column(name = "inventory")
private String inventory;

}
ProductRepository.java
package com.xuguoguo.product.dao;

import com.xuguoguo.product.domain.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor {
}
ProductService.java
package com.xuguoguo.product.service;

import com.xuguoguo.product.domain.Product;

public interface ProductService {

/**
 * 根据id查询
 *
 * @param id
 * @return
 */
Product findById(Long id);


/**
 * 保存
 *
 * @param product
 */
void save(Product product);


/**
 * 更新
 *
 * @param product
 */
void update(Product product);


/**
 * 删除
 *
 * @param id
 */
void delete(Long id);

}
ProductServiceImpl.java
package com.xuguoguo.product.service.impl;

import com.xuguoguo.product.dao.ProductRepository;
import com.xuguoguo.product.domain.Product;
import com.xuguoguo.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;

@Service
@Transactional
public class ProductServiceImpl implements ProductService {

@Autowired
private ProductRepository productRepository;

@Override
public Product findById(Long id) {
    return productRepository.findById(id).orElse(new Product());
}

@Override
public void save(Product product) {
    productRepository.save(product);
}

@Override
public void update(Product product) {
    productRepository.save(product);
}

@Override
public void delete(Long id) {
    productRepository.deleteById(id);
}

}
ProductController.java
package com.xuguoguo.product.controller;

import com.xuguoguo.product.domain.Product;
import com.xuguoguo.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = “/product”)
public class ProductController {

@Autowired
private ProductService productService;

@PostMapping(value = "/save")
public String save(@RequestBody Product product) {
    productService.save(product);
    return "新增成功";
}

@GetMapping(value = "/findById/{id}")
public Product findById(@PathVariable(value = "id") Long id) {
    Product product = productService.findById(id);

    return product;
}

}
启动类:
package com.xuguoguo.product;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Product9003Application {
public static void main(String[] args) {
SpringApplication.run(Product9003Application.class, args);
}
}
4.2.3 订单微服务

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

spring_cloud_demo
com.xuguoguo
1.0

4.0.0

<artifactId>order-service-consul9004</artifactId>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

application.yml server: port: 9004 # 微服务的端口号

spring:
application:
name: service-order # 微服务的名称
datasource:
url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
jmx:
unique-names: true

微服务info内容详细信息

info:
app.name: xxx
company.name: xxx
build.artifactId: zookeeper被什么替代了 zookeeper替代方案_eureka
build.version: zookeeper被什么替代了 zookeeper替代方案_zookeeper_02

开启日志debug

logging:
level:
root: info
SpringConfig.java
package com.xuguoguo.order.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class SpringConfig {

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

}
Product.java
package com.xuguoguo.order.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = “tb_product”)
public class Product implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "product_name")
private String productName;

@Column(name = "status")
private Integer status;

@Column(name = "price")
private BigDecimal price;

@Column(name = "product_desc")
private String productDesc;

@Column(name = "caption")
private String caption;

@Column(name = "inventory")
private String inventory;

}
OrderController.java
package com.xuguoguo.order.controller;

import com.xuguoguo.order.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping(value = “/order”)
public class OrderController {
@Autowired
private RestTemplate restTemplate;

@GetMapping(value = "/buy/{id}")
public Product buy(@PathVariable(value = "id") Long id) {
    Product product = restTemplate.getForObject("http://localhost:9003/product/findById/" + id, Product.class);
    return product;
}

}
启动类:
package com.xuguoguo.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Order9004Application {
public static void main(String[] args) {
SpringApplication.run(Order9004Application.class, args);
}
}
4.3 服务注册

4.3.1 在商品微服务中添加SpringCloud基于Consul的依赖

修改部分:


org.springframework.cloud spring-cloud-starter-consul-discovery 完整部分: <?xml version="1.0" encoding="UTF-8"?> spring_cloud_demo com.xuguoguo 1.0 4.0.0

<artifactId>product_serivce-consul9003</artifactId>

<dependencies>
    <!--    服务监控    -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--   SpringCloud提供的基于Consul的服务发现     -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

4.3.2 在商品微服务的application.yml中配置服务注册

修改部分:
spring:

开始配置Consul的服务注册

cloud:
consul:
# ConsulServer的主机地址
host: 192.168.32.100
# ConsulServer端口
port: 8500
discovery:
# 是否注册
register: true
# 服务实例id 必须填写 也可以写成 {spring.application.name}😒{spring.cloud.client.ip-address}
instance-id: ${spring.application.name}-1
# 服务实例名称
service-name: ${spring.application.name}
# 服务实例端口
port: ${server.port}
# 健康检查路径
health-check-path: /actuator/health
# 健康检查时间间隔
health-check-interval: 15s
# 开启IP注册
prefer-ip-address: true
# 实例的请求IP
ip-address: ${spring.cloud.client.ip-address}
完整部分:
server:
port: 9003 # 微服务的端口号

spring:
application:
name: service-product # 微服务的名称
datasource:
url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql

开始配置Consul的服务注册

cloud:
consul:
# ConsulServer的主机地址
host: 192.168.32.100
# ConsulServer端口
port: 8500
discovery:
# 是否注册
register: true
# 服务实例id 必须填写 也可以写成 {spring.application.name}😒{spring.cloud.client.ip-address}
instance-id: ${spring.application.name}-1
# 服务实例名称
service-name: ${spring.application.name}
# 服务实例端口
port: ${server.port}
# 健康检查路径
health-check-path: /actuator/health
# 健康检查时间间隔
health-check-interval: 15s
# 开启IP注册
prefer-ip-address: true
# 实例的请求IP
ip-address: ${spring.cloud.client.ip-address}

微服务info内容详细信息

info:
app.name: xxx
company.name: xxx
build.artifactId: zookeeper被什么替代了 zookeeper替代方案_eureka
build.version: zookeeper被什么替代了 zookeeper替代方案_zookeeper_02
4.3.3 启动类上加上@EnableDiscoveryClient注解

启动类:
package com.xuguoguo.product;

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

@SpringBootApplication
@EnableDiscoveryClient
public class Product9003Application {
public static void main(String[] args) {
SpringApplication.run(Product9003Application.class, args);
}
}
4.4 服务发现

4.4.1 在订单微服务中添加SpringCloud基于Consul的依赖

修改部分:


org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-consul-discovery 完整部分: <?xml version="1.0" encoding="UTF-8"?> spring_cloud_demo com.xuguoguo 1.0 4.0.0

<artifactId>order-service-consul9004</artifactId>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--    服务监控    -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--   SpringCloud提供的基于Consul的服务发现     -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>
</dependencies>

4.4.2 在订单微服务的application.yml中配置服务注册

修改部分:
spring:

开始配置Consul的服务注册

cloud:
consul:
# ConsulServer的主机地址
host: 192.168.32.100
# ConsulServer端口
port: 8500
discovery:
# 是否注册
register: true
# 服务实例id 必须填写 也可以写成 {spring.application.name}😒{spring.cloud.client.ip-address}
instance-id: ${spring.application.name}-1
# 服务实例名称
service-name: ${spring.application.name}
# 服务实例端口
port: ${server.port}
# 健康检查路径
health-check-path: /actuator/health
# 健康检查时间间隔
health-check-interval: 15s
# 开启IP注册
prefer-ip-address: true
# 实例的请求IP
ip-address: ${spring.cloud.client.ip-address}
完整部分:
server:
port: 9004 # 微服务的端口号

spring:
application:
name: service-order # 微服务的名称
datasource:
url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
jmx:
unique-names: true

开始配置Consul的服务注册

cloud:
consul:
# ConsulServer的主机地址
host: 192.168.32.100
# ConsulServer端口
port: 8500
discovery:
# 是否注册
register: true
# 服务实例id 必须填写 也可以写成 {spring.application.name}😒{spring.cloud.client.ip-address}
instance-id: ${spring.application.name}-1
# 服务实例名称
service-name: ${spring.application.name}
# 服务实例端口
port: ${server.port}
# 健康检查路径
health-check-path: /actuator/health
# 健康检查时间间隔
health-check-interval: 15s
# 开启IP注册
prefer-ip-address: true
# 实例的请求IP
ip-address: ${spring.cloud.client.ip-address}

微服务info内容详细信息

info:
app.name: xxx
company.name: xxx
build.artifactId: zookeeper被什么替代了 zookeeper替代方案_eureka
build.version: zookeeper被什么替代了 zookeeper替代方案_zookeeper_02

开启日志debug

logging:
level:
root: info
4.4.3 在商品微服务的RestTemplate上面标注@LoadBalanced注解

SpringConfig.java
package com.xuguoguo.order.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class SpringConfig {

/**
 * SpringCloud对Consul进行了进一步的处理,向其中集成了Ribbon的支持
 *
 * @return
 */
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

}
4.4.4 修改Controller

OrderController.java
package com.xuguoguo.order.controller;

import com.xuguoguo.order.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping(value = “/order”)
public class OrderController {
@Autowired
private RestTemplate restTemplate;

@GetMapping(value = "/buy/{id}")
public Product buy(@PathVariable(value = "id") Long id) {
    Product product = restTemplate.getForObject("http://service-product/product/findById/" + id, Product.class);
    return product;
}

}
4.4.5 启动类上加@EnableDiscoveryClient注解

启动类:
package com.xuguoguo.order;

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

@EnableDiscoveryClient
@SpringBootApplication
public class Order9004Application {
public static void main(String[] args) {
SpringApplication.run(Order9004Application.class, args);
}
}
5 Consul集群的搭建

5.1 Consul的架构简介

图中的Server是Consul服务端高可用集群,Client是Consul的客户端。Consul客户端不保存数据,客户端将接收到的请求转发给响应的Server端。Server之间通过局域网或广域网通信实现数据一致性。每个Server或Client都是一个Consul agent。Consul集群间使用了gossip协议和raft一致性算法。
上图涉及到的相关术语:
agent:agent是用来启动一个consul的守护进程。
client:是consul的代理,用来和consul server进行交互。一个微服务对应一个client,微服务和client部署到一台机器上。
server:一个server是一个具有一组扩展功能的代理,这些功能包括参与raft选举、维护集群状态、响应RPC查询,和其他数据中心交互以及转发查询给Leader或者远程数据中心等。简而言之。server就是真正干活的consul服务。一般推荐3~5个。
gossip协议:流言协议。所有的consul都会参与到gossip协议中。

raft协议:保证server集群的数据一致。
Leader:处理所有客户端交互、日志复制等,一般一次只有一个Leader。
Follower:类似选民,完全被动。
Candidate(候选人):可以被选为一个新的领导人。
Leader全权负责所有客户端的请求,以及将数据同步到Follower中(同一时刻系统中只存在一个Leader)。
Follower被动响应请求RPC,从不主动发起RPC。
Candidate由Follower向Leader转换中间状态。
5.2 Consul集群搭建

5.2.1 概述

首先一个正常的Consul集群,有Server,有Client。这里的服务器Server1、Server2和Server3上分别部署了Consul Server(这些服务器上最好只部署Consul程序,以尽量维护Consul Server的稳定)。
服务器Server4和Server5上通过Consul Client分别注册Service A、B、C,这里每个Service分别注册在了两个服务器上,这样可以避免Service的单点问题(一般而言微服务和Client绑定)。
在服务器Server6中Service D需要访问ServiceB,这个时候Service D首先访问本机Consul Client提供的HTTP API,本机Client会将请求转发到Consul Server,Consul Server查询到Service B当前的信息返回。

5.2.2 准备环境
服务器IPConsul类型Node节点名称序号192.168.237.100Serverserver-1s1192.168.237.101Serverserver-2s2192.168.237.102Serverserver-3s3192.168.237.1Clientclient-1s4

简而言之,就是通过VMWear新建3个CentOS7环境,然后每个CentOS7环境中的网络模式都是NAT。

agent以client模式启动的节点:在该模式下,该节点会采集相关信息,通过RPC的方式向server发送。client模式节点有无数个,官方建议搭配微服务配置。
agent以server模式启动的节点:一个数据中心至少包含1个server节点。不过官网建议使用3~5个server节点组成集群,以保证高可用且不失效率。server节点参与raft、维护微服务信息、注册服务、健康检查等功能。
5.2.3 安装consul并启动

关闭每个consul节点上(Linux机器)上的防火墙:
systemctl stop firewalld
systemctl disable firewalld
在每个consul节点上安装consul服务,下载安装过程和单节点一致:
从官网下载最新版本的consul服务
wget https://releases.hashicorp.com/consul/1.8.4/consul_1.8.4_linux_amd64.zip
使用unzip命令解压
unzip consul_1.8.4_linux_amd64.zip
将解压好的consul可执行命令赋值到/usr/local/bin目录下
cp consul /usr/local/bin
测试一下
consul
启动每个consul server节点:
consul agent -server -bootstrap-expect 3 -data-dir=/etc/consul.d -node=server-1 -bind=192.168.237.100 -ui -client 0.0.0.0 &

consul agent -server -bootstrap-expect 3 -data-dir=/etc/consul.d -node=server-2 -bind=192.168.237.101 -ui -client 0.0.0.0 &

consul agent -server -bootstrap-expect 3 -data-dir=/etc/consul.d -node=server-3 -bind=192.168.237.102 -ui -client 0.0.0.0 &
-server:以server身份启动
-bootstrap-expect:集群要求的最少Server数量,当低于这个数量,集群将失效
-data-dir:data存放的目录。
-node:节点id,在同一集群中不能重复。
-bind:监听的IP地址。
-client:客户端的IP地址(0.0.0.0)表示不限制。
&:在后台运行,Linux脚本语法

在本地电脑中使用client形式启动consul:
consul agent -data-dir /etc/consul.d -node=client-1 -bind=192.168.237.1 -client=0.0.0.0
5.2.4 每个节点加入到集群中

在s2(192.168.237.101),s3(192.168.237.102)和s4(192.168.237.1)服务行通过consul join命令加入s1中的consul集群中。
加入到consul集群
consul join 192.168.237.100
5.2.5 测试

在任意一台服务器中输入consul members查看集群中的所有节点信息:
查看consul集群节点信息
consul members

通过浏览器http://192.168.237.100:8500/ui/dc1/nodes查看:

5.3 微服务注册到Consul集群中

5.3.1 订单微服务注册到Consul集群中

application.yml
server:
port: 9003 # 微服务的端口号

spring:
application:
name: service-product # 微服务的名称
datasource:
url: jdbc:mysql://192.168.237.100:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql

开始配置Consul的服务注册

cloud:
consul:
# Consul Client的地址 修改的地方
host: 192.168.237.1
# ConsulServer端口
port: 8500
discovery:
# 是否注册
register: true
# 服务实例id 必须填写 也可以写成 {spring.application.name}😒{spring.cloud.client.ip-address}
instance-id: ${spring.application.name}-1
# 服务实例名称
service-name: ${spring.application.name}
# 服务实例端口
port: ${server.port}
# 健康检查路径
health-check-path: /actuator/health
# 健康检查时间间隔
health-check-interval: 15s
# 开启IP注册
prefer-ip-address: true
# 实例的请求IP
ip-address: ${spring.cloud.client.ip-address}

微服务info内容详细信息

info:
app.name: xxx
company.name: xxx
build.artifactId: zookeeper被什么替代了 zookeeper替代方案_eureka
build.version: zookeeper被什么替代了 zookeeper替代方案_zookeeper_02
5.3.2 商品微服务注册到Consul集群中

application.yml
server:
port: 9004 # 微服务的端口号

spring:
application:
name: service-order # 微服务的名称
datasource:
url: jdbc:mysql://192.168.237.100:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
jmx:
unique-names: true

开始配置Consul的服务注册

cloud:
consul:
# Consul Client的地址 修改的地方
host: 192.168.237.1
# ConsulServer端口
port: 8500
discovery:
# 是否注册
register: true
# 服务实例id 必须填写 也可以写成 {spring.application.name}😒{spring.cloud.client.ip-address}
instance-id: ${spring.application.name}-1
# 服务实例名称
service-name: ${spring.application.name}
# 服务实例端口
port: ${server.port}
# 健康检查路径
health-check-path: /actuator/health
# 健康检查时间间隔
health-check-interval: 15s
# 开启IP注册
prefer-ip-address: true
# 实例的请求IP
ip-address: ${spring.cloud.client.ip-address}

微服务info内容详细信息

info:
app.name: xxx
company.name: xxx
build.artifactId: zookeeper被什么替代了 zookeeper替代方案_eureka
build.version: zookeeper被什么替代了 zookeeper替代方案_zookeeper_02

开启日志debug

logging:
level:
root: info
5.4 Consul的常见问题

5.4.1 节点和服务注销

当服务或者节点失效,Consul不会对注册的信息进行剔除处理,仅仅进行标记而已(并且服务不可以使用)。如果担心失效节点和失效服务过多影响监控。可以通过调用HTTP API的形式进行处理。
节注销任意节点和服务:
PUT请求
http://192.168.32.100:8500/v1/catalog/deregister 注销当前节点的服务:
PUT请求
http://192.168.32.100:8500/v1/agent/service/deregister/[service-id]
如果某个节点不继续使用,也可以在本机使用consul leave命令,或者在其他节点使用consul force-level 节点id。

5.4.2 健康检查和故障转移

在集群环境下,健康检查是由服务注册到的agent来处理的,如果这个agent挂掉了,那么此节点的健康检查就处于无人管理的状态了。
从实际应用看,节点上的服务可能既要被发现,又要发现别的服务,如果节点挂掉了,仅提供被发现的功能实际上服务还是不可用的。当然发现别的服务也可以不使用本机节点,可以通过访问一个Nginx实现的若干Consul节点的负载均衡来实现。

  1. List item