最近在学习SpringCloud框架,首先接触的应该就是注册中心,目前SpringCloud常用的几个注册中心包括:Eureka、Zookeeper、Nacos和Consul。

目前我在本地搭建SpringCloud demo时主要使用Eureka进行调试,后期学习Nacos时,再添加Nacos相关知识~

一、服务注册中心

在微服务架构中,随着业务越来越复杂,系统中的服务节点会变得越俩越多,为了更好地管理架构中的服务节点,就需要服务注册中心。服务注册中心提供了服务注册、服务发现、服务管理、负载均衡等能力。

服务的提供方和服务的消费者都需要将自己的信息注册到服务注册中心中,服务注册中心会保存所有服务,当服务的消费者需要调用服务时,会根据服务列表,通过负载均衡算法,选择服务提供方的相应地址进行服务调用。

Eureka

官网目前已经停止更新了~

介绍

Netflix开发的服务发现框架,基于REST的服务。

提供了两个组件:Eureka Server和Eureka Client,分别对应服务的provider和服务的consumer。Eureka的架构比较清晰和简单,如下图所示:

Eureka架构图

包含三个角色:Eureka服务中心、服务消费者和服务提供者。

Eureka服务中心可以进行集群部署,集群中各个节点属于平级关系。服务消费者和提供者启动时向Eureka Server注册服务,并保持心跳连接,默认为90秒。

Eureka的自我保护机制:当服务发生一些故障时,如网络分区故障,默认是15分钟内收到的续约低于原来的85%时,这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server仍能接收新服务的注册和查询请求,但是不会同步到其他节点上;同时也会保护服务注册表中的信息,不再移除注册列表中因为长时间没收到心跳而应该过期的服务。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。这样获取的数据的确有可能不是最新的,但Eureka的这种自我保护机制,极大地保证了Eureka的高可用特性。

下图是Eureka的前端界面,其中CLOUD-PROVIDER-HYSTRIX-PAYMENT是我本地注册的一个服务,如下图:

本地搭建

这里简单介绍下单机的主要配置。

Eureka Server

application.yml配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server:
port: 7001

eureka:
instance:
hostname: eureka7001.com # 集群域名
client:
# 不向注册中心注册自己
register-with-eureka: false
fetch-registry: false
service-url:
# 集群
# defaultZone: http://eureka7002.com:7002/eureka/
# 单机
defaultZone: http://eureka7001.com:7001/eureka/

如果配置集群环境,则在server-url中配置集群其他节点的地址,可以理解为相互注册

在主方法中添加@EnableEurekaServer注解即可:

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaServer // 表示当前是Eureka的服务注册中心
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args);
}
}
Service Provider

在注册服务时,application.yml可以如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 8001

spring:
application:
name: cloud-payment-service # 服务名称
...

eureka:
client:
# 表示将自己注册进EurekaServer,默认为true
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka

在主函数中添加@EnableEurekaClient注解

Service Consumer和Service Provider类似,就不描述了。

Nacos

1. 简介

一个更易于构建云原生应用的动态服务发现、配置管理和服务的管理平台。他其实融合了配置中心和服务注册中心,包含了原先Eureka和Config的功能。

Nacos:Dynamic Naming and Configuration Service

2. 本地搭建

在官网下载安装后,通过命令startup.cmd -m standalone可以运行单机模式,

然后在本地创建新的工程,并在pom中引入坐标,工程的yml则按照官网描述来配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 9001

spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 配置Nacos地址,注册到Nacos

management:
endpoints:
web:
exposure:
include: '*'

主启动类添加@EnableDiscoveryClient注解

业务类Controller写个简单的GET即可,我们分别在9001端口和9002端口,配置两个服务端。

然后我们创建服务消费端工程,在nacos的依赖中包含了ribbon的依赖,因此Nacos自带了负载均衡。

服务端的配置application.yml,注意微服务名称的唯一性,不能写错,建议直接copy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 80

spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848

#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider

在主启动类只需要添加注解@EnableDiscoveryClient即可。

业务类需要引入RestTemplate

1
2
3
4
5
6
7
8
9
@Configuration
public class ApplicationContextBean {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@Slf4j
public class OrderNacosController {

@Resource
private RestTemplate restTemplate;

@Value("${service-url.nacos-user-service}")
private String serverURL;

@GetMapping(value = "/consumer/payment/nacos/{id}")
public String paymentInfo(@PathVariable("id") Long id) {
String result = restTemplate.getForObject(serverURL + "/payment/nacos/" + id, String.class);
log.info(result);
return result;
}
}

最后我们创建一个配置服务,需要添加坐标:

1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

工程的yml有两个,一个为全局的,一个为项目的,分别为bootstrap.yml和application.yml

bootstrap.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# nacos配置
server:
port: 3377

spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置

application.yml

1
2
3
4
spring:
profiles:
# active: dev # 表示开发环境
active: test # 表示测试环境

在主启动类同样添加@EnableDiscoveryClient注解即可。

在业务类通过Spring Cloud原生注解@RefreshScope实现配置自动更新:

1
2
3
4
5
6
7
8
9
10
11
@RestController
@RefreshScope //在控制器类加入@RefreshScope注解使当前类下的配置支持Nacos的动态刷新功能。
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;

@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
}

下面简单测试一下,首先看Nacos的管理页面:

上创建的1个消费端、两个服务提供者,第三个是配置服务端。

我们通过http://localhost/consumer/payment/nacos/1,在消费端调用服务接口:

然后我们在Nacos的配置中心添加测试配置:

在之前的application.yml配置中,我们设置为test环境:

1
2
3
4
spring:
profiles:
# active: dev # 表示开发环境
active: test # 表示测试环境

最后我们可以通过访问·ttp://localhost:3377/config/info查看配置信息:

3. Nacos集群部署

本地搭建时还没研究Nacos的集群部署,挖个坑,后期补上!

To be continue…

4. 小结

Nacos融合了之前的Eureka和Config,并且仍在持续更新和维护中,社区活跃度也很高。

二、注册中心选型

平时在学习相似的技术、框架或组件时,要考虑其之间的不同特性。在实际工作项目中,需要结合具体的业务场景去做综合全面的选择,比如是更看重系统的一致性,还是需要保证系统的高可用

由于目前在本地学习时主要使用Eureka作为注册中心,下面参考了一篇总结注册中心选型的文章,做了一些整理,主要对比了Eureka、Zookeeper、Nacos以及Consul四个主流的注册中心。

参考链接

1.集群结构

  • Eureka的集群架构本身就是平级结构,集群各节点都是平起平坐的关系,数据是相互复制的,因此各个节点都是主人角色

  • zookeeper和consul则均为主从结构。集群角色包含leader、follower以及observer三类,具体来说是一主多从结构,就是有一个leader,多个follower,以及只负责读操作、不参与选举的observer

  • Nacos则支持平级关系和主从这两种集群架构,常用的是后者。包含leader、follower、candidate三类。Leader:负责Client交互和log复制,同一时刻系统中最多存在1个。Follower:被动响应请求RPC,从不主动发起请求RPC。接收到请求,会转发给leader处理。Candidate:一种临时的角色,只存在于leader的选举阶段。

    某个节点想要变成leader,那么就发起投票请求,同时自己变成candidate,如果选举成功,则变为candidate,否则退回为follower

2 是否可以及时知道服务状态变化

由于zookeeper具有watcher机制,因此可以及时知道数据目录的状态变化,那么也就可以及时知道服务器节点以及所注册的实例的变化。我们可以通过这种监听机制,能够实时获取到某个服务器的故障或者我们比较关心的节点数据的更(zookeeper的节点znode包含服务器节点和数据节点)

其他三个注册中心没有这样的机制,只是可以通过管理端进行主动查看服务状态,并不能实时感知服务状态的变化

3 一致性协议(CAP)

回顾一下CAP理论:

1)一致性(Consistency)注册中心的集群各个节点的数据信息完全一致

2)可用性(Availability)每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据

3)分区容错性(Partition tolerance)以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择

任何一个分布式系统都不能同时实现上述三点,一般都会实现P即分区容错性,然后在A和P之前进行权衡和取舍。

Eureka :支持AP,即保障可用性和分区容错性

Zookeeper:支持CP,即保障一致性和分区容错性

consul:支持CP,即保障一致性和分区容错性

nacos:支持AP和CP两种模式。可以分别支持AP和CP两种场景

  • 一般来说,如果不需要存储服务级别的信息,且服务实例是通过Nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。AP模式为了服务的可用性而减弱了一致性,因此AP模式下只能注册临时实例
  • 如果需要在服务级别编辑或存储配置信息,那么CP是必须,K8S服务和DNS服务则适用于CP服务,CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误

4 雪崩保护

  • Eureka有雪崩保护。我们知道,当网络分区故障发生时,微服务与Eureka Server之间突然无法正常通信了,根据心跳机制,微服务将会被注销。那么这种心跳机制是不是就变得不太友好了?因为这种情况下微服务本身其实是健康的,本不应该注销这个微服务,因此Eureka就提供了一个自我保护机制,也就是雪崩保护机制
  • Zookeeper和consul没有雪崩保护
  • Nacos有雪崩保护。为了防止因过多实例 (Instance) 不健康导致流量全部流向健康实例 (Instance) ,继而造成流量压力把健康实例 (Instance) 压垮并形成雪崩效应,应将健康保护阈值定义为一个0到1之间的浮点数。当域名健康实例占总服务实例的比例小于该值时,无论实例是否健康,都会将这个实例返回给客户端。这样做虽然损失了一部分流量,但是保证了集群的剩余健康实例 (Instance) 能正常工作

5 负载均衡

Eureka: 使用ribbon实现

Zookeeper: 一般可以直接采用RPC的负载均衡

Nacos: 采用权重/metadata/Selector

Consul: 使用Fabio

小结:

​ 需要结合业务场景来进行选择。比如说,对于金融类的业务场景,对于一致性要求更高,那么就会排除掉Eureka,然后根据易用性、性价比等其他方面再进行后续的选择;对于高可用比较注重的项目,如电商类项目,则可以选择Eurek或者Nacos,但再比较其他方面,Nacos不仅可以做注册中心,还可以作为架构中的配置中心,并且社区活跃度比较高,功能也日渐在完善,使用的人越来越多,因此综合来讲,就选择了Nacos
​ 具体选择的过程中,当然也会考虑这些因素之外的一些特点,包括人员的熟悉度等等,但肯定也是先考虑主要的特点,再去考虑这些不是那么重要的特点的。