01:服务注册与发现+配置中心-Nacos+Eureka

一:Eureka

1.1:Eureka简介

Eureka是基于REST(Representational State Transfer)服务,主要以AWS云服务为支撑,提供服务发现并实现负载均衡和故障转移。我们称此服务为Eureka服务。

Eureka提供了Java客户端组件,Eureka Client,方便与服务端的交互。客户端内置了基于round-robin实现的简单负载均衡。

在Netflix,为Eureka提供更为复杂的负载均衡方案进行封装,以实现高可用,它包括基于流量、资源利用率以及请求返回状态的加权负载均衡。

Eureka包含两个组件:Eureka Server和Eureka Client

1.1.1:Eureka Server提供服务注册服务

各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

1.1.2:EurekaClient通过注册中心进行访问

是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。

如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)

1.2:Eureka架构

1.3:Eureka中的一些概念

1.3.1:Register :服务注册

Eureka客户端向Eureka Server注册时,它提供自身的元数据,比如IP地址、端口等

1.3.2:Renew:服务续约

Eureka客户端会每隔30秒发送一次心跳来续约。通过续约来告知Eureka Server该客户端仍然存在。

1.3.3:Fetch Registries:获取注册列表信息

Eureka客户端从服务器获取注册表信息,将其缓存到本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒)更新一次。

1.3.4:Cancel:服务下线

Eureka客户端在程序关闭时向Eureka服务器发送取消请求。

1.3.5:Eviction:服务剔除

在默认情况下,当Eureka客户端90秒没有向Eureka服务器发送续约,Eureka服务器就会将该服务实例从服务注册列表删除。

1.3.6:自我保护

Eureka会定时剔除没有续约的服务,那就可能会出现一种极端情况,网络发生了异常,所有的服务都没有续约,那么EurekaServer就会剔除掉所有的服务,这显然不合理,于是就有了自我保护机制,当短时间内(15min)如果续约失败的比例达到了设置的阈值(85%),就会触发自我保护机制,该机制下,EurekaServer不会剔除服务,等到正常后再退出自我保护机制。自我保护开关(eureka.server.enable-self-preservation=true)。

除了以上的特性外,Eureka的缓存机制也非常经典,下面详细介绍一下。

1.3.7:Eureka Server 数据存储

Eureka Server 的数据存储分了两层:数据存储层和缓存层。

数据存储层 记录注册到 Eureka Server 上的服务信息,缓存层是经过包装后的数据,可以直接在 Eureka Client 调用时返回。

Eureka Server 的数据存储层是双层的 ConcurrentHashMap

第一层的 ConcurrentHashMap 的 key=spring.application.name 也就是客户端实例注册的应用名;value 为嵌套的 ConcurrentHashMap。

第二层嵌套的 ConcurrentHashMap 的 key=instanceId 也就是服务的唯一实例 ID,value 为 Lease 对象,Lease 对象存储着这个实例的所有注册信息,包括 ip 、端口、属性等。

Eureka Server 缓存机制

Eureka Server 为了提供响应效率,提供了两层的缓存结构,将 Eureka Client 所需要的注册信息,直接存储在缓存结构中。

第一层缓存:readOnlyCacheMap,本质上是 ConcurrentHashMap,依赖定时从readWriteCacheMap 同步数据,默认时间为 30 秒。

readOnlyCacheMap : 是一个 CurrentHashMap 只读缓存,这个主要是为了供客户端获取注册信息时使用,其缓存更新,依赖于定时器的更新,通过和 readWriteCacheMap 的值做对比,如果数据不一致,则以 readWriteCacheMap 的数据为准。

第二层缓存:readWriteCacheMap,本质上是 Guava 缓存。

readWriteCacheMap 的数据主要同步于存储层。当获取缓存时判断缓存中是否没有数据,如果不存在此数据,则通过 CacheLoader 的 load 方法去加载,加载成功之后将数据放入缓存,同时返回数据。

readWriteCacheMap 缓存过期时间,默认为 180 秒,当服务下线、过期、注册、状态变更,都会来清除此缓存中的数据。

Eureka Client 获取全量或者增量的数据时,会先从一级缓存中获取;如果一级缓存中不存在,再从二级缓存中获取;如果二级缓存也不存在,这时候先将存储层的数据同步到缓存中,再从缓存中获取。

1.4:Eureka Server常用配置

java 复制代码
#服务端开启自我保护模式
eureka.server.enable-self-preservation=true
#扫描失效服务的间隔时间(单位毫秒,默认是60*1000)即60秒
eureka.server.eviction-interval-timer-in-ms= 60000
#间隔多长时间,清除过期的 delta 数据
eureka.server.delta-retention-timer-interval-in-ms=0
#请求频率限制器
eureka.server.rate-limiter-burst-size=10
#是否开启请求频率限制器
eureka.server.rate-limiter-enabled=false
#请求频率的平均值
eureka.server.rate-limiter-full-fetch-average-rate=100
#是否对标准的client进行频率请求限制。如果是false,则只对非标准client进行限制
eureka.server.rate-limiter-throttle-standard-clients=false
#注册服务、拉去服务列表数据的请求频率的平均值
eureka.server.rate-limiter-registry-fetch-average-rate=500
#设置信任的client list
eureka.server.rate-limiter-privileged-clients=
#在设置的时间范围类,期望与client续约的百分比。
eureka.server.renewal-percent-threshold=0.85
#多长时间更新续约的阈值
eureka.server.renewal-threshold-update-interval-ms=0
#对于缓存的注册数据,多长时间过期
eureka.server.response-cache-auto-expiration-in-seconds=180
#多长时间更新一次缓存中的服务注册数据
eureka.server.response-cache-update-interval-ms=0
#缓存增量数据的时间,以便在检索的时候不丢失信息
eureka.server.retention-time-in-m-s-in-delta-queue=0
#当时间戳不一致的时候,是否进行同步
eureka.server.sync-when-timestamp-differs=true
#是否采用只读缓存策略,只读策略对于缓存的数据不会过期。
eureka.server.use-read-only-response-cache=true


################server node 与 node 之间关联的配置#####################33
#发送复制数据是否在request中,总是压缩
eureka.server.enable-replicated-request-compression=false
#指示群集节点之间的复制是否应批处理以提高网络效率。
eureka.server.batch-replication=false
#允许备份到备份池的最大复制事件数量。而这个备份池负责除状态更新的其他事件。可以根据内存大小,超时和复制流量,来设置此值得大小
eureka.server.max-elements-in-peer-replication-pool=10000
#允许备份到状态备份池的最大复制事件数量
eureka.server.max-elements-in-status-replication-pool=10000
#多个服务中心相互同步信息线程的最大空闲时间
eureka.server.max-idle-thread-age-in-minutes-for-peer-replication=15
#状态同步线程的最大空闲时间
eureka.server.max-idle-thread-in-minutes-age-for-status-replication=15
#服务注册中心各个instance相互复制数据的最大线程数量
eureka.server.max-threads-for-peer-replication=20
#服务注册中心各个instance相互复制状态数据的最大线程数量
eureka.server.max-threads-for-status-replication=1
#instance之间复制数据的通信时长
eureka.server.max-time-for-replication=30000
#正常的对等服务instance最小数量。-1表示服务中心为单节点。
eureka.server.min-available-instances-for-peer-replication=-1
#instance之间相互复制开启的最小线程数量
eureka.server.min-threads-for-peer-replication=5
#instance之间用于状态复制,开启的最小线程数量
eureka.server.min-threads-for-status-replication=1
#instance之间复制数据时可以重试的次数
eureka.server.number-of-replication-retries=5
#eureka节点间间隔多长时间更新一次数据。默认10分钟。
eureka.server.peer-eureka-nodes-update-interval-ms=600000
#eureka服务状态的相互更新的时间间隔。
eureka.server.peer-eureka-status-refresh-time-interval-ms=0
#eureka对等节点间连接超时时间
eureka.server.peer-node-connect-timeout-ms=200
#eureka对等节点连接后的空闲时间
eureka.server.peer-node-connection-idle-timeout-seconds=30
#节点间的读数据连接超时时间
eureka.server.peer-node-read-timeout-ms=200
#eureka server 节点间连接的总共最大数量
eureka.server.peer-node-total-connections=1000
#eureka server 节点间连接的单机最大数量
eureka.server.peer-node-total-connections-per-host=10
#在服务节点启动时,eureka尝试获取注册信息的次数
eureka.server.registry-sync-retries=
#在服务节点启动时,eureka多次尝试获取注册信息的间隔时间
eureka.server.registry-sync-retry-wait-ms=
#当eureka server启动的时候,不能从对等节点获取instance注册信息的情况,应等待多长时间。
eureka.server.wait-time-in-ms-when-sync-empty=0

1.5:Eureka Client 常用配置

java 复制代码
#该客户端是否可用
eureka.client.enabled=true
#实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true
eureka.client.register-with-eureka=false
#此客户端是否获取eureka服务器注册表上的注册信息,默认为true
eureka.client.fetch-registry=false
#是否过滤掉,非UP的实例。默认为true
eureka.client.filter-only-up-instances=true
#与Eureka注册服务中心的通信zone和url地址
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

#client连接Eureka服务端后的空闲等待时间,默认为30 秒
eureka.client.eureka-connection-idle-timeout-seconds=30
#client连接eureka服务端的连接超时时间,默认为5秒
eureka.client.eureka-server-connect-timeout-seconds=5
#client对服务端的读超时时长
eureka.client.eureka-server-read-timeout-seconds=8
#client连接all eureka服务端的总连接数,默认200
eureka.client.eureka-server-total-connections=200
#client连接eureka服务端的单机连接数量,默认50
eureka.client.eureka-server-total-connections-per-host=50
#执行程序指数回退刷新的相关属性,是重试延迟的最大倍数值,默认为10
eureka.client.cache-refresh-executor-exponential-back-off-bound=10
#执行程序缓存刷新线程池的大小,默认为5
eureka.client.cache-refresh-executor-thread-pool-size=2
#心跳执行程序回退相关的属性,是重试延迟的最大倍数值,默认为10
eureka.client.heartbeat-executor-exponential-back-off-bound=10
#心跳执行程序线程池的大小,默认为5
eureka.client.heartbeat-executor-thread-pool-size=5
# 询问Eureka服务url信息变化的频率(s),默认为300秒
eureka.client.eureka-service-url-poll-interval-seconds=300
#最初复制实例信息到eureka服务器所需的时间(s),默认为40秒
eureka.client.initial-instance-info-replication-interval-seconds=40
#间隔多长时间再次复制实例信息到eureka服务器,默认为30秒
eureka.client.instance-info-replication-interval-seconds=30
#从eureka服务器注册表中获取注册信息的时间间隔(s),默认为30秒
eureka.client.registry-fetch-interval-seconds=30

# 获取实例所在的地区。默认为us-east-1
eureka.client.region=us-east-1
#实例是否使用同一zone里的eureka服务器,默认为true,理想状态下,eureka客户端与服务端是在同一zone下
eureka.client.prefer-same-zone-eureka=true
# 获取实例所在的地区下可用性的区域列表,用逗号隔开。(AWS)
eureka.client.availability-zones.china=defaultZone,defaultZone1,defaultZone2
#eureka服务注册表信息里的以逗号隔开的地区名单,如果不这样返回这些地区名单,则客户端启动将会出错。默认为null
eureka.client.fetch-remote-regions-registry=
#服务器是否能够重定向客户端请求到备份服务器。 如果设置为false,服务器将直接处理请求,如果设置为true,它可能发送HTTP重定向到客户端。默认为false
eureka.client.allow-redirects=false
#客户端数据接收
eureka.client.client-data-accept=
#增量信息是否可以提供给客户端看,默认为false
eureka.client.disable-delta=false
#eureka服务器序列化/反序列化的信息中获取"_"符号的的替换字符串。默认为"__"
eureka.client.escape-char-replacement=__
#eureka服务器序列化/反序列化的信息中获取"$"符号的替换字符串。默认为"_-"
eureka.client.dollar-replacement="_-"
#当服务端支持压缩的情况下,是否支持从服务端获取的信息进行压缩。默认为true
eureka.client.g-zip-content=true
#是否记录eureka服务器和客户端之间在注册表的信息方面的差异,默认为false
eureka.client.log-delta-diff=false
# 如果设置为true,客户端的状态更新将会点播更新到远程服务器上,默认为true
eureka.client.on-demand-update-status-change=true
#此客户端只对一个单一的VIP注册表的信息感兴趣。默认为null
eureka.client.registry-refresh-single-vip-address=
#client是否在初始化阶段强行注册到服务中心,默认为false
eureka.client.should-enforce-registration-at-init=false
#client在shutdown的时候是否显示的注销服务从服务中心,默认为true
eureka.client.should-unregister-on-shutdown=true

1.6:Eureka Instance 常用配置

java 复制代码
#服务注册中心实例的主机名
eureka.instance.hostname=localhost
#注册在Eureka服务中的应用组名
eureka.instance.app-group-name=
#注册在的Eureka服务中的应用名称
eureka.instance.appname=
#该实例注册到服务中心的唯一ID
eureka.instance.instance-id=
#该实例的IP地址
eureka.instance.ip-address=
#该实例,相较于hostname是否优先使用IP
eureka.instance.prefer-ip-address=false

1.7:Eureka注册中心组成

Eureka属于AP架构,高可用。

1.8:EurekaServer端源码--流程图

1:找到eurekaserver jar包,spi机制,找到其中的sping.factories文件,找到server服务端自动装配的类EurekaServerAutoConfiguration

2:此配置类中会配置一些Bean

  • EurekaServerConfig---初始化eureka相关配置
  • EurekaController---初始化接口,获取eurekaServer信息
  • PeerAwareInstanceRegistry---初始化集群注册表
  • PeerEurekaNodes---初始化集群节点集合
  • EurekaServerContext---基于eurekaServer注册配置表,集群节点集合及初始化eurekaServer上下文
  • EurekaServerBootstrap---启动eureka服务
  • FilterRegistrationBean---初始化Jersey过滤器

3:此配置类中@Import注解的实现类EurekaServerInitializerConfiguration,实现了SmartLifecycle接口并且定义了isAutoStartup返回true,SmartLifecycle继承自Lifecycle,就会调用到Lifecycle的start方法

SmartLifecycle继承了Lifecycle,LifecycleProcessor继承了Lifecycle并且其中定义了onRefresh方法,在spring启动时刷新上下文时候就会调用到此方法。调用链路如下:

  • spring启动刷新上下文------this.finishRefresh();
  • LifecycleProcessor().onRefresh();
  • this.startBeans(true);
  • DefaultLifecycleProcessor.this.doStart
  • start
  • start方法中新起了一个线程,启动eureka服务
  • 初始化上下文------this.eurekaServerBootstrap.contextInitialized(this.servletContext);
  • 发布eureka注册有效事件------this.publish(new EurekaRegistryAvailableEvent(this.getEurekaServerConfig()));
  • 发布eureka服务启动成功事件------this.publish(new EurekaServerStartedEvent(this.getEurekaServerConfig()));
  • contextInitialized方法中
    • 初始化eureka环境------this.initEurekaEnvironment();
    • 初始化上下文------this.initEurekaServerContext();
  • 初始化上下文------this.initEurekaServerContext()中------服务同步replicate
    • 从相邻节点复制注册表------this.registry.syncUp();
    • 获取相邻节点注册的服务实例------Applications apps = this.eurekaClient.getApplications();
    • 注册到本地------this.register(instance, instance.getLeaseInfo().getDurationInSecs(), true);
  • 初始化上下文------this.initEurekaServerContext()中------服务剔除evict
    • registry.openForTraffic(this.applicationInfoManager, registryCount);
    • 修改服务实例状态为UP------applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
    • 开启一个定时任务,每隔60s清理没有续约的任务------postInit();
    • 定时任务------
    java 复制代码
    this.evictionTimer.schedule((TimerTask)this.evictionTaskRef.get(), 
                                this.serverConfig.getEvictionIntervalTimerInMs(), 
                                this.serverConfig.getEvictionIntervalTimerInMs());
    • 定时任务run方法-----EvictionTask.run()------com.netflix.eureka.registry.AbstractInstanceRegistry.EvictionTask#run

1.9:EurekaClient端源码--流程图

1.9.1:如何开启服务注册进Eureka服务

EurekaClient的开启可以使用@EnableDiscoveryClient注解,也可以不使用这个注解,因为这个注解中的属性autoRegister默认为TRUE,如果我们不想某个服务注册进Eureka可以将这个属性设置为false(@EnableDiscoveryClient(autoRegister= false))

1.9.2:Client端流程

  • EurekaClientAutoConfiguration中注解@AutoConfigureAfter中含有一个类EurekaDiscoveryClientConfiguration,
  • 这个类中会注入一个bean-Marker。此Marker正是EurekaClientAutoConfiguration所需要的,只有这个bean存在springboot才会在启动的时候注册这个EurekaClientAutoConfiguration-Bean.
  • EurekaClientAutoConfiguration中配置了很多的Bean EurekaClientConfigBean------初始化EurekaClient相关配置

    EurekaInstanceConfigBean------client实例相关配置信息

    EurekaServiceRegistry------服务注册

    EurekaHealthIndicator------心跳检测

    EurekaClient

  • 其中EurekaClient很重要,EurekaClient是对DiscoveryClient包装。
  • DiscoveryClient的构造函数中会初始化各种任务,在initScheduledTasks中会初始化定时拉取服务注册信息更新本地注册表和服务续约任务;初始化定时服务注册任务。后续具体流程请查看流程图。

二:Nacos

2.1:安装并运行Nacos

2.2:服务注册中心对比

2.1.1:Nacos 支持AP和CP模式的切换

C是所有节点在同一时间看到的数据是一致的;而A的定义是所有的请求都会收到响应。

C:Consistency(强一致性)

A:Availability(可用性)

P:Partition tolerance(分区容错性)

CAP理论关注粒度是数据,而不是整体系统设计的策略

//-----------------------------------------------------------------------------------------------------

最多只能同时较好的满足两个。

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,

因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:

CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。

CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。

AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

2.1.2:何时选择使用何种模式?

一般来说,

如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如 Spring cloud 和 Dubbo 服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。

如果需要在服务级别编辑或者存储配置信息,那么 CP 是必须,K8S服务和DNS服务则适用于CP模式。

CP模式下则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。

相关推荐
一路向北·重庆分伦4 小时前
04:服务网关Spring Cloud Gateway
spring cloud
JiaHao汤8 小时前
微服务注册中心深度解析:Eureka、Consul、Nacos 从原理到实战
spring cloud·微服务·eureka·consul
一路向北·重庆分伦11 小时前
02:远程调用与负载均衡-RestTemplate+OpenFeign+Spring Cloud LoadBalancer
spring cloud
indexsunny2 天前
互联网大厂Java面试实战:从Spring Boot到微服务架构的深度解析
java·spring boot·spring cloud·kafka·prometheus·security·microservices
隔壁小邓2 天前
SpringCloud微服务拆分原则
spring cloud·微服务·架构
深念Y3 天前
Nginx和Spring Cloud Gateway
运维·服务器·网络·网关·nginx·spring cloud·微服务
WZTTMoon4 天前
Spring Boot 启动报错:OpenFeign 隐性循环依赖,排查了整整一下午
java·spring boot·后端·spring cloud·feign
冬天豆腐4 天前
Springcloud,Nacos管理,打jar包后,启动报错
java·spring cloud·maven·jar
爱吃山竹的大肚肚4 天前
依赖冲突快速解决
java·spring boot·后端·spring cloud·maven