红泥小火炉


SpringCloud注册中心Eureka

Nathaniel 2022-07-09 72浏览 0条评论
首页/正文
分享到: / / / /

SpringCloud-Eureka

Eureka基本信息

用于服务注册与发现的基础组件,含有Eureka客户端和Eureka服务端;

在设计时,Eureka遵循的是AP原则,即保障了可用性和分区容错性;

基础应用

1.添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

2.在启动类上添加@EnableEurekaServer注解

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

3.修改配置文件中的eureka相关信息

server.port=8761
eureka.instance.hostname=localhost
eureka.instance.instance-id=eureka-server-instance
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
spring.application.name=eureka-server

4.客户端(生产者和消费者)添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

5.客户端-生产者或消费者相关配置

eureka.instance.instance-id=eureka-client-producer
eureka.instance.hostname=localhost
eureka.client.service-url.defalutZone=http://localhost:8761/eureka/
server.port=8080
spring.application.name=eureka-client-producer

基本规则

服务发现

客户端启动之后,会先从服务端处获取注册表信息,并将其缓存在本地,用于后期调用;

相关核心类或者接口:客户端---DiscoveryClient接口

EurekaDiscoveryClient类实现了DiscoveryClient接口;其内部组合了EurekaClient实现相关功能:

EurekaClient类的默认实现类为DiscoveryClient,实现了LookupService接口:

LookupService接口提供以下能力:

Application getApplication(String appName); // 根据appName获取到application;
Applications getApplications(); // 获取到所有的注册的application;
List<InstanceInfo> getInstancesById(String id); // 根据id获取到对应的服务实例信息;
InstanceInfo getNextServerFromEureka(String virtualHostname, boolean secure); // 从服务端获取下一个可能的服务实例;
// Application中持有多个InstanceInfo信息,相当于是一个同名的application下存在多个集群节点InstanceInfo,Application对InstanceInfo的操作均为同步操作。

在进行服务发现前,先拉取服务器上的注册表信息

调用DiscoveryClient#fetchRegistry(boolean forceFullRegistryFetch):

1.判断需要全量拉取还是增量拉取
	如果是第一次拉取服务,则为全量,调用DiscoveryClient#getAndStoreFullRegistry();
	如果不是第一次拉取,则为增量,调用DiscoveryClient#getAndUpdateDelta(applications);
2.计算并封装applications的哈希码,用于Eureka服务器和客户端之间的实例对比,调用applications.setAppsHashCode(applications.getReconcileHashCode());
3.打印客户端中服务实例的总数:logTotalInstances();
4.在更新远程实例状态之前通知缓存刷新:onCacheRefreshed();
5.根据缓存数据更新远程实例状态:updateInstanceRemoteStatus();

至此,Eureka客户端即拉取到了服务器上的注册表信息;

服务注册

服务提供者启动之后,调用Eureka的方法向Eureka服务器注册自己的信息。Eureka服务端也会维护一个注册的服务列表;当服务实例状态发生变化之后,会向Eureka服务器更新自己的状态,同时会向其他Eureka服务器节点作状态同步。

eureka.client.register-with-eureka=true // true表示向Eureka注册自身的实例状态信息

相关方法:客户端---DiscoveryClient#register();

通过RestTemplateEurekaHttpClient发起注册请求,返回注册成功,成功状态码为204 NO_CONTENT;

服务端---AbstractInstanceRegistry#register();

在服务端,所有注册的服务实例均存在ConcurrentHashMap<String, Map<String, Lease>>中;其为一个map,以应用名称映射到一个Map结构,内层的map由应用实例id映射到Lease结构(可以代表应用实例的InstanceInfo);

1.对整个注册过程加read锁;
2.根据应用名称,获取到对应的应用实例信息;
3.如果能获取到2中的应用实例信息,就比较当前注册实例和2中获取到的应用实例的最后更新时间,取最后更新时间大的应用实例;
4.如果在2中无法获取到应用实例信息,则表示这次注册是一个全新的注册过程,就创建新的应用实例信息,放入注册表中;

服务续约

Eureka客户端每30秒向Eureka服务端发送一次心跳,通过心跳来实现续约操作(先更新自身状态,再同步其他Eureka服务器);

Eureka服务器在默认情况下,如果90秒内没有收到客户端服务实例的心跳,那么会将该服务实例从注册列表中删除;

eureka.instance.lease-expiration-duration-in-seconds=90
eureka.instance.lease-renewal-interval-in-seconds=30

Eureka客户端维护了三个定时任务来处理与服务端之间的心跳续约等操作;

相关方法:客户端---DiscoveryClient#initScheduledTasks

1.使用ScheduledExecutorService创建并执行向Eureka服务端拉取注册表信息刷新本地缓存任务,任务调用CacheRefreshThread#refreshRegistry();
2.心跳定时器:每隔30s执行一次心跳检测任务,调用HeartbeatThread#renew(),在该方法中发起心跳检测请求,并对服务端不存在的服务实例进行重新注册;
3.InstanceInfo复制器:内部实现了一个定时器用于定时刷新服务实例的信息和检查应用状态的变化,即通过InstanceInfoReplicator实现;实现了一个状态改变的监听器,当服务实例状态或者应用状态发生变化之后,发起监听通知notify(StatusChangeEvent statusChangeEvent),通过onDemandUpdate方法来实现状态上报,上报方式沿用该InstanceInfo复制器中的定时器;

服务端---AbstractInstanceRegistry#renew();

1.根据应用名称和实例id尝试获取下注册表中的注册信息;
2.如果1中无结果,则返回false,不做续约处理;
3.如果1中有结果,则判断状态是否为UNKNOWN,如果不是,则进行续约操作,更新租约中的时间;

服务下线

客户端下线时,会先向服务端发送下线请求;服务端收到请求之后,会将该服务实例从注册列表中删除;

相关方法:客户端---DiscoveryClient#shutdown();

在调用shutdown之后,会进行响应的清理工作;
1.注销ApplicationInfoManager中的状态监听;
2.取消定时任务cancelScheduledTasks();
3.改变服务状态为下线applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN),同时发起取消注册unregister();
4.关闭通信客户端eurekaTransport.shutdown();
5.关闭相应的监视器

服务端---AbstractInstanceRegistry#internalCancel()

相关操作与注册过程类似,先找到对应注册表信息中的应用实例信息,再删除对应的应用实例;

自我保护

在该机制下Eureka服务器会保护注册列表中的信息,不会删除任何服务实例;

自我保护开启的条件:每分钟收到的心跳数量低于指定的阈值。

阈值=服务实例总量 * (60/单个实例心跳间隔时长(秒)) * 保护系数(0.85)

默认情况下,如果有15%的服务实例心跳丢失,Eurek服务端就会进入自我保护模式;

eureka.server.enable-self-preservation=false // 禁用自我保护机制
eureka.server.renewal-percent-threshold=0.85 // 自我保护系数

Eureka集群

Eureka集群采用的方式是两两注册,不能单项二次注册,需要注意!

# 节点1
eureka.client.service-url.defaultZone=http://eureka.server.back1:8762/eureka,http://eureka.server.back2:8763/eureka
# 节点2
eureka.client.service-url.defaultZone=http://eureka.server:8761/eureka,http://eureka.server.back2:8763/eureka
# 节点3
eureka.client.service-url.defaultZone=http://eureka.server:8761/eureka,http://eureka.server.back1:8762/eureka

上述生产者端的服务注册地址设置为:

eureka.client.service-url.defaultZone=http://eureka.server:8761/eureka,http://eureka.server.back2:8763/eureka,http://eureka.server.back1:8762/eureka

启动以上三个节点和生产者端服务,访问节点2上的Eureka界面:

停掉集群中的生产者端最初连接的节点服务,可以看到在重试失败之后,Eureka客户端会自动切换至正常的其他节点服务,可以实现故障迁移和负载均衡。

最后修改:2022-07-09 12:02:06 © 著作权归作者所有
上一篇

评论列表

还没有人评论哦~赶快抢占沙发吧~