1. Eureka
1. 概述
什么是服务治理
Spring Cloud封装了 Netflix 公司开发的Eureka模块来实现服务治理
在传统的RPC远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册
什么是服务注册与发现
Eureka采用了C/S的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程
调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何RPC远程框架中,都会有一个注册中心存放服务地址相关信息(接口地址)
Eureka包含两个组件
- Eureka Server提供服务注册服务:各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
- EurekaClient通过注册中心进行访问:它是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
2. EurekaServer服务端配置
IDEA生成eurekaServer端服务注册中心,类似物业公司
1. 创建名为cloud-eureka-server7001的Maven工程
2. 修改pom.xml
<!-- eureka新旧版本 --><!-- 以前的老版本(2018)--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</artifactId></dependency><!-- 现在新版本(2020.2)--><!-- 我们使用最新的 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>
<?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">
<parent>
<artifactId>SpringCloud</artifactId>
<groupId>org.gmf</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>org.gmf</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--boot web actuator-->
<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.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
3. 添加application.yaml
server:
port: 7001
eureka:
instance:
hostname: locathost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4. 添加主启动类
@SpringBootApplication
//开启Eureka服务注册中心功能
@EnableEurekaServer
public class EurekaMian7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMian7001.class, args);
}
}
5. 测试
测试运行 EurekaMain7001,浏览器输入 http://localhost:7001/,查看Spring Eureka服务主页
3. 支付微服务8001入驻进EurekaServer
EurekaClient端cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider,类似学校对外提供授课服务
1. 修改cloud-provider-payment8001 的pom.xml文件
添加spring-cloud-starter-netflix-eureka-client依赖
<!--Eureka服务提供端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2. 修改application.yaml文件
添加Eureka服务提供端配置
spring:
application:
# 服务注册到Eureka的名称
name: cloud-payment-service
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
3. 主启动类添加@EnableEurekaClient注解
@SpringBootApplication
//开启Eureka客户端服务,将该服务注册到Eureka中
@EnableEurekaClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}
4. 测试
- 启动cloud-provider-payment8001和cloud-eureka-server7001工程。
- 浏览器输入 - http://localhost:7001/ 主页内的Instances currently registered with Eureka会显示cloud-provider-payment8001的配置文件application.yml设置的应用名cloud-payment-service

注意:以上红字不是报错,而是Eureka的自我保护机制
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARELESSER THAN THRESHOLD AND HENCFT ARE NOT BEING EXPIRED JUST TO BE SAFE.
紧急情况!EUREKA可能错误地声称实例在没有启动的情况下启动了。续订小于阈值,因此实例不会为了安全而过期
4. 订单微服务80入驻进EurekaServer
EurekaClient端cloud-consumer-order80将注册进EurekaServer成为服务消费者consumer,类似来上课消费的同学
1. 修改pom.xml
<!--Eureka服务提供端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2. 修改yaml
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
3. 主启动类添加@EnableEurekaClient注解
4. 测试
启动cloud-provider-payment8001、cloud-eureka-server7001和cloud-consumer-order80这三工程
5. Eureka集群原理说明

- 服务注册:将服务信息注册进注册中心
- 服务发现:从注册中心上获取服务信息
- 实质:存key服务命取value闭用地址
注册流程:
- 先启动eureka注册中心
- 启动服务提供者payment支付服务
- 支付服务启动后会把自身信息(比如服务地址)以别名方式注朋进eureka
- 消费者order服务在需要调用接口时,使用服务别名去注册中心获取实际的RPC远程调用地址
- 消去者导调用地址后,底层实际是利用HttpClient技术实现远程调用
- 消费者获得服务地址后会缓存在本地jvm内存中,默认每间隔30秒更新—次服务调用地址
问题:微服务RPC远程服务调用最核心的是什么
高可用,试想你的注册中心只有一个only one,万一它出故障了,会导致整个为服务环境不可用
解决办法:搭建Eureka注册中心集群,实现负载均衡+故障容错
Eureka集群之间互相注册,互相守望
6. Eureka集群环境构建
1. 创建cloud-eureka-server7002工程,过程参考创建server7001步骤
2. 找到C:\Windows\System32\drivers\etc路径下的hosts文件,修改映射配置添加进hosts文件
作用:可通过指定域名访问到特定IP(测试用作区分Eureka服务)
#======SpringCloud=======
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
3. 修改cloud-eureka-server7001配置文件
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。(单机)
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群之间互相注册绑定,指向其他的Eureka
defaultZone: http://eureka7002.com:7002/eureka
4. 修改cloud-eureka-server7002配置文件
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。(单机)
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群之间互相注册绑定,指向其他的Eureka
defaultZone: http://eureka7001.com:7001/eureka
5. 测试
测试访问http://eureka7002.com:7002/
测试访问http://eureka7001.com:7001/
7. 将微服务注册进Eureka集群
将支付服务8001微服务,订单服务80微服务发布到上面2台Eureka集群配置中
修改它们的application.yaml配置文件项 eureka.client.service-url.defaultZone
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#集群地址使用 , 分隔
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
测试:
访问http://eureka7002.com:7002/
、
8. 建立微服务集群
微服务业务部分同样需要做类似负载均衡集群的配置,以保证高可用的能力
1. 新建cloud-provider-payment8002支付微服务
步骤参考cloud-provider-payment8001,application.yaml修改端口以及启动类

2. 修改cloud-provider-payment8001/8002的controller,用作展示区分
@Slf4j
@RestController
@RequestMapping("/payment")
public class PaymentController {
@Autowired
private PaymentService paymentService;
//获取配置文件中的端口设置,用作展示区分
@Value("${server.port}")
private String prot;
@PostMapping("/create")
public CommonResult<Integer> create(@RequestBody Payment payment){
int result = paymentService.create(payment);
log.info("插入结果:"+result);
if(result > 0)
{
return new CommonResult(200,"插入数据库成功"+prot,result);
}else{
return new CommonResult(444,"插入数据库失败",null);
}
}
@GetMapping("/getByid/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
if (payment!=null){
return new CommonResult(200,"查询成功"+prot,payment);
}else {
return new CommonResult(444,"没有查询到对应记录",null);
}
}
}
3. 设置消费微服务访问
当配置完以上步骤之后,再次查看Eureka服务注册中心(发现已经存在两个生产端微服务)
查看消费端代码,访问的路径永远是固定的8001
设置其通过Eureka注册中心服务名调用微服务
4. RestTemplate负载均衡设置
当配置完了以上步骤后,浏览器通过消费端获取数据 http://localhost/consumer/payment/get/1
错误解析:未设置RestTemplate负载均衡功能
修改配置类
@Configuration
public class ApplicationContextConfiguration {
/**
* @param
* @return org.springframework.web.client.RestTemplate
* @description 配置类中注册RestTemplate,通过其进行远程调用
*/
@Bean
//使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
5. 测试
浏览器访问http://localhost/consumer/payment/get/1
结果:负载均衡效果达到,8001/8002端口交替出现
总结:相互注册相互守望
9. Actuator微服务信息完善
1. 修改服务实例名称
访问服务注册中心后,发现我们所有的服务实例名称都是由 IP地址+应用名称 组成
我们可以将其换成可读性更高的名称
生产微服务修改application.yaml配置文件设置服务实例名称
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#集群地址使用 , 分隔
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
instance:
#设置服务实例名称
instance-id: payment8001
修改之后eureka主页将显示payment8001,payment8002代替原来显示的IP地址
2. 服务实例IP信息提示
鼠标悬浮于payment8002服务实例名称后,浏览器左下角显示
application.yaml
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#集群地址使用 , 分隔
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
instance:
#设置服务实例名称
instance-id: payment8001
#设置IP显示
prefer-ip-address: true
10. 服务发现Discovery
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
1. 修改cloud-provider-payment8001的Controller
@Slf4j
@RestController
@RequestMapping("/payment")
public class PaymentController {
//引入import org.springframework.cloud.client.discovery.DiscoveryClient
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping
public DiscoveryClient discovery(){
List<String> services = discoveryClient.getServices();
services.forEach((service)->{
log.info("*****element: "+service);
});
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
instances.forEach((instance)->{
log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
});
return discoveryClient;
}
...
}
2. 修改cloud-provider-payment8001的主启动类
@SpringBootApplication
//开启Eureka客户端服务,将该服务注册到Eureka中
@EnableEurekaClient
//添加注解
@EnableDiscoveryClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}
3. 测试
浏览器访问http://localhost:8001/payment/discovery
查看日志打印
11. Eureka自我保护
1. 自我保护机制概述
为什么会产生Eureka自我保护机制?
为了EurekaClient可以正常运行,防止与EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除
什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
自我保护机制:默认情况下EurekaClient定时向EurekaServer端发送心跳包
如果Eureka在server端在一定时间内(默认90秒)没有收到EurekaClient发送心跳包,便会直接从服务注册列表中剔除该服务,但是在短时间( 90秒中)内丢失了大量的服务实例心跳,这时Eurekaserver会开启自我保护机制,不会剔除该服务(该现象可能出现在如果网络不通但是EurekaClient为出现宕机,此时如果换做别的注册中心如果一定时间内没有收到心跳会将剔除该服务,这样就出现了严重失误,因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的)。
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着。
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
参数配置参考https://www.jianshu.com/p/e2bebfb0d075
2. 禁用自我保护
在eurekaServer端7001处设置关闭自我保护机制
出厂默认,自我保护机制是开启的
- eureka.server.enable-self-preservation = false:可以禁用自我保护模式
- eureka.server.eviction-interval-timer-in-ms:eureka server清理无效节点的时间间隔(默认60秒)
eureka: ... server: #关闭自我保护机制,保证不可用服务被及时踢除 enable-self-preservation: false #eureka server清理无效节点的时间间隔 #private long evictionIntervalTimerInMs = 60000L; 默认60秒 eviction-interval-timer-in-ms: 2000
启动禁用了自我保护的cloud-eureka-server7001,页面提示自我保护关闭
THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS 自我保护模式是关闭的。这可能不会保护实例过期的情况下,网络/其他问题
生产者客户端eureakeClient端8001
eureka.instance.lease-renewal-interval-in-seconds:表示eureka client发送心跳给server端的频率。如果在leaseExpirationDurationInSeconds后,server端没有收到client的心跳,则将摘除该instance。除此之外,如果该instance实现了HealthCheckCallback,并决定让自己unavailable的话,则该instance也不会接收到流量
- 默认30秒
eureka.instance.lease-expiration-duration-in-seconds:表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance
- 默认为90秒
- 如果该值太大,则很可能将流量转发过去的时候,该instance已经不存活了。
- 如果该值设置太小了,则instance则很可能因为临时的网络抖动而被摘除掉。
- 该值至少应该大于leaseRenewalIntervalInSeconds
eureka:
...
instance:
...
#心跳检测与续约时间
#开发时没置小些,保证服务关闭后注册中心能即使剔除服务
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 2
启动cloud-provider-payment8001,再关闭其模拟出现网络动荡导致心跳包发送延迟
结论:发现服务关闭后,EurekaServer中很快将实例清除了
12. Eureka停更说明
https://github.com/Netflix/eureka/wiki
Eureka 2.0 (Discontinued) The existing open source work on eureka 2.0 is discontinued. The code base and artifacts that were released as part of the existing repository of work on the 2.x branch is considered use at your own risk.
Eureka 1.x is a core part of Netflix’s service discovery system and is still an active project.
2. Zookeeper
1. 准备环境
zookeeper是一个分布式协调工具,可以实现注册中心功能
我们需要在CentOS中安装Zookeeper,并启动运行
Docker安装Zookeeper
ZooKeeper学习笔记
1. 创建名为cloud-provider-payment8004的Maven工程
2. 修改pom.xml
<?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">
<parent>
<artifactId>SpringCloud</artifactId>
<groupId>org.gmf</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8004</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- SpringBoot整合zookeeper客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<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.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.gmf</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
3. 编写application.yaml
#8004表示注册到zookeeper服务器的支付服务提供者端口号
server:
port: 8004
#服务别名----注册zookeeper到注册中心名称
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
#设置zookeeper服务地址
connect-string: 192.168.31.254:2181
4. 主启动类
@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class, args);
}
}
5. 编写Controller
@Slf4j
@RestController
@RequestMapping("/payment")
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/zk")
public String paymentzk(){
return "springcloud with zookeeper: "+serverPort+"\t"+ UUID.randomUUID().toString();
}
}
6. 测试
运行启动cloud-provider-payment8004
浏览器访问:http://localhost:8004/payment/zk

服务器Zookeeper查看服务
3. Consul
1. Consul介绍
What is Consul? Consul is a service mesh solution providing a full featured control plane with service discovery, configuration, and segmentation functionality. Each of these features can be used individually as needed, or they can be used together to build a full service mesh. Consul requires a data plane and supports both a proxy and native integration model. Consul ships with a simple built-in proxy so that everything works out of the box, but also supports 3rd party proxy integrations such as Envoy. link
Consul是一个服务网格解决方案,它提供了一个功能齐全的控制平面,具有服务发现、配置和分段功能。这些特性中的每一个都可以根据需要单独使用,也可以一起用于构建全服务网格。Consul需要一个数据平面,并支持代理和本机集成模型。Consul船与一个简单的内置代理,使一切工作的开箱即用,但也支持第三方代理集成,如Envoy。
consul 英 [ˈkɒnsl] 美 [ˈkɑːnsl] n. 领事
Consul是一套开源的分布式服务发现和配置管理系统,由HashiCorp 公司用Go语言开发。
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
它具有很多优点。包括:基于raft协议,比较简洁;支持健康检查,同时支持HTTP和DNS协议支持跨数据中心的WAN集群提供图形界面跨平台,支持Linux、Mac、Windows
The key features of Consul are:
- Service Discovery: Clients of Consul can register a service, such as api or mysql, and other clients can use Consul to discover providers of a given service. Using either DNS or HTTP, applications can easily find the services they depend upon.
- Health Checking: Consul clients can provide any number of health checks, either associated with a given service (“is the webserver returning 200 OK”), or with the local node (“is memory utilization below 90%”). This information can be used by an operator to monitor cluster health, and it is used by the service discovery components to route traffic away from unhealthy hosts.
- KV Store: Applications can make use of Consul’s hierarchical key/value store for any number of purposes, including dynamic configuration, feature flagging, coordination, leader election, and more. The simple HTTP API makes it easy to use.
- Secure Service Communication: Consul can generate and distribute TLS certificates for services to establish mutual TLS connections. Intentions can be used to define which services are allowed to communicate. Service segmentation can be easily managed with intentions that can be changed in real time instead of using complex network topologies and static firewall rules.
- Multi Datacenter: Consul supports multiple datacenters out of the box. This means users of Consul do not have to worry about building additional layers of abstraction to grow to multiple regions.
Consul的主要特点是:
- 服务发现 - 提供HTTP和DNS两种发现方式。
- 健康监测 - 支持多种方式,HTTP、TCP、Docker、Shell脚本定制化
- KV存储 - Key、Value的存储方式
- 多数据中心 - Consul支持多数据中心
- 可视化Web界面
2. 安装并运行Consul
相关站点
拉取 Consul 镜像
# 拉取最新版本的镜像
# docker pull consul:latest
# 拉取特定版本的镜像
# docker pull consul:1.7.3
Docker 安装 Consul(单机)
# 创建并启动容器,默认是以开发模式启动,数据保存在内存中
# docker run -d --name=consul -e CONSUL_BIND_INTERFACE=eth0 -p 8500:8500 consul:1.7.3
# 查看容器的运行状态
[root@GMF ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
28bdb3513e97 consul:1.7.3 "docker-entrypoint.s…" 12 seconds ago Up 11 seconds 8300-8302/tcp, 8301-8302/udp, 8600/tcp, 8600/udp, 0.0.0.0:8500->8500/tcp consul
浏览器访问可视化页面
http://192.168.31.254:8500/
3. 服务提供者注册进Consul
1. 新建Module支付服务provider8006
2. pom.xml
<?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">
<parent>
<artifactId>SpringCloud</artifactId>
<groupId>org.gmf</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8006</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>org.gmf</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--SpringCloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3. application.yaml
###consul服务端口号
server:
port: 8006
spring:
application:
name: consul-provider-payment
####consul注册中心地址
cloud:
consul:
host: 192.168.31.254
port: 8500
discovery:
#hostname: 127.0.0.1
service-name: ${spring.application.name}
4. 业务Controller
@Slf4j
@RestController
@RequestMapping("/payment")
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping(value = "/consul")
public String paymentConsul()
{
return "springcloud with consul: "+serverPort+"\t "+ UUID.randomUUID().toString();
}
}
5. 主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class, args);
}
}
6. 测试
运行启动cloud-provider-payment8006
浏览器Consul可视化界面查看
4. 服务消费者注册进Consul
1. 新建Module消费服务order80 - cloud-consumerconsul-order80
2. pom.xml
<?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">
<parent>
<artifactId>SpringCloud</artifactId>
<groupId>org.gmf</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumerconsul-order80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--SpringCloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3. application.yaml
###consul服务端口号
server:
port: 80
spring:
application:
name: cloud-consumer-order
####consul注册中心地址
cloud:
consul:
host: 192.168.31.254
port: 8500
discovery:
#hostname: 127.0.0.1
service-name: ${spring.application.name}
4. 主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class OrderConsulMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderConsulMain80.class, args);
}
}
5. 配置类
@Configuration
public class ApplicationContextConfiguration {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
6. Controller
@Slf4j
@RestController
@RequestMapping("/consumer/payment")
public class OrderConsulController {
public static final String INVOKE_URL = "http://consul-provider-payment";
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/consul")
public String paymentInfo()
{
String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
return result;
}
}
7. 测试
运行consul,cloud-providerconsul-payment8006,cloud-consumerconsul-order80
查看Consul可视化页面
浏览器访问http://localhost/consumer/payment/consul,成功响应
4. 三个注册中心异同点
| 组件名 | 语言 | CAP | 服务健康检查 | 对外暴露接口 |
|---|---|---|---|---|
| Eureka | Java | AP | 可配支持 | HTTP |
| Consul | Go | CP | 支持 | HTTP/DNS |
| Zookeeper | Java | CP | 支持 | 客户端 |
CAP:
- C:Consistency (强一致性)
- A:Availability (可用性)
- P:Partition tolerance (分区容错性)

最多只能同时较好的满足两个
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求
因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
- CA - 单点集群,满足—致性,可用性的系统,通常在可扩展性上不太强大。
- CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。
- AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
CP 与 AP 对立同一的矛盾关系
AP架构(Eureka)
当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。
结论:违背了一致性C的要求,只满足可用性和分区容错,即AP、
CP架构(Consul、Zookeeper)
当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性。
结论:违背了可用性A的要求,只满足一致性和分区容错,即CP
