当前位置:网站首页>实战监听Eureka client的缓存更新

实战监听Eureka client的缓存更新

2022-06-23 07:43:00 InfoQ

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):
https://github.com/zq2599/blog_demos

从Eureka server获取服务列表

  • Spring cloud环境中的应用,如果注册到Eureka server,就会从Eureka server获取所有应用的注册信息(也叫服务列表),然后保存到本地,这个操作是周期性的,默认每三十秒一次;
  • 以下是来自官方的架构图,可以看到Application Service向Eureka Server有Get Registry的请求:

null

参考文章

  • 如果您有兴趣,想深入了解
    spring广播机制
    或者
    Eureka client更新服务列表
    ,推荐您参考以下两篇文章:

  • 《spring4.1.8扩展实战之三:广播与监听 》
  • 《Spring Cloud源码分析之Eureka篇第五章:更新服务列表 》

实战内容

  • 本文是一篇实战的文章,实战内容如下:

  • 启动Eureka server;
  • 开发一个应用springcloudcustomizelistener,启动后会注册到Eureka server;
  • 此时该应用身份为Eureka client,会周期性的从Eureka server获取服务列表(已有逻辑);
  • 每次成功获取的服务列表成功都会存入本地缓存(已有逻辑);
  • 存入缓存后,会在spring容器内发送广播(已有逻辑);
  • 本次实战的重点就是自定义一个监听器来接收上述广播,收到广播后把详情用日志打印出来;
  • 再启动另一个应用springclouddeepprovider,也会注册到Eureka server;
  • 再去观察springcloudcustomizelistener的广播监听日志,会发现springclouddeepprovider的注册信息;

Eureka client缓存服务列表的源码简介

  • 实战前,先对Eureka client缓存服务列表的实现源码做个简介,这样才能做出匹配的监听器;
  • 应用作为Eureka Client的启动时,在com.netflix.discovery.DiscoveryClient类的initScheduledTasks方法中,会启动周期性任务,每隔30秒从Eureka server获取服务列表信息,如下图,红框中的TimedSupervisorTask负责周期性执行,绿框中的CacheRefreshThread负责具体的更新逻辑:

null
  • 在CacheRefreshThread类中经过层层调用,获取服务列表并更新本地缓存的逻辑在fetchRegistry方法中实现,如下图,红框中的getAndStoreFullRegistry方法负责全量更新,绿框中的getAndUpdateDelta方法负责增量更新,黄框中的onCacheRefreshed方法就是今天的重点:发送广播,广播类型是
    服务列表的本地缓存已更新

null
  • onCacheRefreshed方法在子类CloudEurekaClient中被重写,可见这里发送了一个普通的spring容器内广播,类型是HeartbeatEvent,我们可以自定义监听类来接收广播,并通过泛型规定只接受HeartbeatEvent类型:

 @Override
 protected void onCacheRefreshed() {
 if (this.cacheRefreshedCount != null) { //might be called during construction and will be null
 long newCount = this.cacheRefreshedCount.incrementAndGet();
 log.trace("onCacheRefreshed called with count: " + newCount);
 //spring容器内广播,HeartbeatEvent实例在创建时收到两个参数:CloudEurekaClient实例和缓存刷新次数
 this.publisher.publishEvent(new HeartbeatEvent(this, newCount));
 }
 }

实战应用设定

  • 本次实战要搭建一个小的Spring Cloud环境,包括以下应用:
null

源码下载

  • springclouddeepeureka和springclouddeepprovider这两个应用,在文章《Spring Cloud源码分析之Eureka篇第一章:准备工作》中已有详细介绍,本文中就不多说了,您可以参考文章,也可以在github下载这两个应用的源码,地址和链接信息如下表所示:

null
  • 这个git项目中有多个文件夹,本章源码分别在springclouddeepeureka、springclouddeepprovider这两个文件夹下,如下图红框所示:

null

启动springclouddeepeureka

  • 应用springclouddeepeureka开发完成后就立即启动,在浏览器访问地址:http://localhost:8081,可见Eureka server已经启动,不过还没有任何应用注册上来,如下图:

null

开发springcloudcustomizelistener

  • 接下来一起开发应用springcloudcustomizelistener,在此应用中添加自定义的spring广播监听器,如果您不想敲代码,也可以从github上直接下载源码,地址和链接信息如下表所示:
null
  • 这个git项目中有多个文件夹,本章源码在springcloudcustomizelistener文件夹下,如下图红框所示:

null
  • 一起来开发吧:
  • 创建一个springboot的web应用,pom.xml内容如下,注意为了在日志中展示更详细的内容,依赖了fastjson库:

<?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?>
<project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;>
 <modelVersion>4.0.0</modelVersion>

 <groupId>com.bolingcavalry</groupId>
 <artifactId>springcloudcustomizelistener</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>springcloudcustomizelistener</name>
 <description>Demo project for Spring Boot</description>

 <parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>1.5.9.RELEASE</version>
 </parent>

 <properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 <java.version>1.8</java.version>
 </properties>

 <dependencies>
 <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>

 <dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>fastjson</artifactId>
 <version>1.2.28</version>
 </dependency>

 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 <scope>test</scope>
 </dependency>
 </dependencies>

 <dependencyManagement>
 <dependencies>
 <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-dependencies</artifactId>
 <version>Edgware.RELEASE</version>
 <type>pom</type>
 <scope>import</scope>
 </dependency>
 </dependencies>
 </dependencyManagement>
 
 <build>
 <plugins>
 <plugin>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-maven-plugin</artifactId>
 </plugin>
 </plugins>
 </build>
</project>

  • 应用配置文件application.yml的内容如下:

server:
 port: 8085
spring:
 application:
 name: springcloud-customize-listener
eureka:
 client:
 serviceUrl:
 defaultZone: http://localhost:8081/eureka/
 instance:
 prefer-ip-address: true

  • 创建监听器EurekaCacheRefreshListener.java,前面已经分析过HeartbeatEvent实例的两个成员变量,值为缓存刷新次数和CloudEurekaClient实例,在收到广播时,将这两个成员变量都在日志中打印出来:

@Component
public class EurekaCacheRefreshListener implements ApplicationListener<HeartbeatEvent> {

 private static final Logger logger = LoggerFactory.getLogger(EurekaCacheRefreshListener.class);

 @Override
 public void onApplicationEvent(HeartbeatEvent event) {
 Object count = event.getValue();
 Object source = event.getSource();

 logger.info(&quot;start onApplicationEvent, count [{}], source :\n{}&quot;, count, JSON.toJSON(source));
 }
}

  • 启动应用,等待大约30秒左右,EurekaCacheRefreshListener中的日志就会在控制台输出,内容十分丰富,建议您复制这些内容去在线格式化JSON的网站做一下格式化再来看,下面列出部分关键信息,其他的内容已经略去:

{
 &quot;instanceRemoteStatus&quot;: &quot;UP&quot;,
 &quot;lastSuccessfulHeartbeatTimePeriod&quot;: 316,
 &quot;allKnownRegions&quot;: [
 &quot;us-east-1&quot;
 ],
 ...
 此处省略部分内容
 ...
 &quot;applications&quot;: {
 &quot;appsHashCode&quot;: &quot;UP_1_&quot;,
 &quot;registeredApplications&quot;: [
 {
 &quot;instances&quot;: [
 {
 &quot;hostName&quot;: &quot;192.168.31.104&quot;,
 &quot;overriddenStatus&quot;: &quot;UNKNOWN&quot;,
 &quot;metadata&quot;: {
 &quot;jmx.port&quot;: &quot;64656&quot;,
 &quot;management.port&quot;: &quot;8085&quot;
 },
 &quot;statusPageUrl&quot;: &quot;http://192.168.31.104:8085/info&quot;,
 &quot;secureVipAddress&quot;: &quot;springcloud-customize-listener&quot;,
 &quot;leaseInfo&quot;: {
 &quot;renewalIntervalInSecs&quot;: 30,
 &quot;registrationTimestamp&quot;: 1537827838033,
 &quot;evictionTimestamp&quot;: 0,
 &quot;renewalTimestamp&quot;: 1537827987419,
 &quot;durationInSecs&quot;: 90,
 &quot;serviceUpTimestamp&quot;: 1537827837461
 },
 &quot;homePageUrl&quot;: &quot;http://192.168.31.104:8085/&quot;,
 &quot;countryId&quot;: 1,
 &quot;sID&quot;: &quot;na&quot;,
 &quot;securePort&quot;: 443,
 &quot;dataCenterInfo&quot;: {
 &quot;name&quot;: &quot;MyOwn&quot;
 },
 &quot;instanceId&quot;: &quot;DESKTOP-82CCEBN:springcloud-customize-listener:8085&quot;,
 &quot;coordinatingDiscoveryServer&quot;: false,
 &quot;id&quot;: &quot;DESKTOP-82CCEBN:springcloud-customize-listener:8085&quot;,
 &quot;vIPAddress&quot;: &quot;springcloud-customize-listener&quot;,
 &quot;dirty&quot;: false,
 &quot;lastUpdatedTimestamp&quot;: 1537827838033,
 &quot;healthCheckUrl&quot;: &quot;http://192.168.31.104:8085/health&quot;,
 &quot;appName&quot;: &quot;SPRINGCLOUD-CUSTOMIZE-LISTENER&quot;,
 &quot;lastDirtyTimestamp&quot;: 1537827837401,
 &quot;iPAddr&quot;: &quot;192.168.31.104&quot;,
 &quot;version&quot;: &quot;unknown&quot;,
 &quot;actionType&quot;: &quot;ADDED&quot;,
 &quot;port&quot;: 8085,
 &quot;healthCheckUrls&quot;: [
 &quot;http://192.168.31.104:8085/health&quot;
 ],
 &quot;status&quot;: &quot;UP&quot;
 }
 ]

  • 如上所示,当前应用的实例信息在本地已经缓存了;

启动springclouddeepprovider

  • 应用springclouddeepprovider是最后一个启动的应用,启动该应用后,再去观察springcloudcustomizelistener的日志,发现应用springclouddeepprovider的注册信息已经获取到了:

{
 &quot;instances&quot;: [
 {
 &quot;hostName&quot;: &quot;192.168.119.1&quot;,
 &quot;overriddenStatus&quot;: &quot;UNKNOWN&quot;,
 &quot;metadata&quot;: {
 &quot;jmx.port&quot;: &quot;58420&quot;,
 &quot;management.port&quot;: &quot;8082&quot;
 },
 &quot;statusPageUrl&quot;: &quot;http://192.168.119.1:8082/info&quot;,
 &quot;secureVipAddress&quot;: &quot;springcloud-deep-provider&quot;,
 &quot;leaseInfo&quot;: {
 &quot;renewalIntervalInSecs&quot;: 30,
 &quot;registrationTimestamp&quot;: 1537840715486,
 &quot;evictionTimestamp&quot;: 0,
 &quot;renewalTimestamp&quot;: 1537840715486,
 &quot;durationInSecs&quot;: 90,
 &quot;serviceUpTimestamp&quot;: 1537840715486
 },
 &quot;homePageUrl&quot;: &quot;http://192.168.119.1:8082/&quot;,
 &quot;countryId&quot;: 1,
 &quot;sID&quot;: &quot;na&quot;,
 &quot;securePort&quot;: 443,
 &quot;dataCenterInfo&quot;: {
 &quot;name&quot;: &quot;MyOwn&quot;
 },
 &quot;instanceId&quot;: &quot;localhost:springcloud-deep-provider:8082&quot;,
 &quot;coordinatingDiscoveryServer&quot;: false,
 &quot;id&quot;: &quot;localhost:springcloud-deep-provider:8082&quot;,
 &quot;vIPAddress&quot;: &quot;springcloud-deep-provider&quot;,
 &quot;dirty&quot;: false,
 &quot;lastUpdatedTimestamp&quot;: 1537840715486,
 &quot;healthCheckUrl&quot;: &quot;http://192.168.119.1:8082/health&quot;,
 &quot;appName&quot;: &quot;SPRINGCLOUD-DEEP-PROVIDER&quot;,
 &quot;lastDirtyTimestamp&quot;: 1537840715451,
 &quot;iPAddr&quot;: &quot;192.168.119.1&quot;,
 &quot;version&quot;: &quot;unknown&quot;,
 &quot;actionType&quot;: &quot;ADDED&quot;,
 &quot;port&quot;: 8082,
 &quot;healthCheckUrls&quot;: [
 &quot;http://192.168.119.1:8082/health&quot;
 ],
 &quot;status&quot;: &quot;UP&quot;
 }
 ],
 &quot;name&quot;: &quot;SPRINGCLOUD-DEEP-PROVIDER&quot;,
 &quot;instancesAsIsFromEureka&quot;: [
 {
 &quot;hostName&quot;: &quot;192.168.119.1&quot;,
 &quot;overriddenStatus&quot;: &quot;UNKNOWN&quot;,
 &quot;metadata&quot;: {
 &quot;jmx.port&quot;: &quot;58420&quot;,
 &quot;management.port&quot;: &quot;8082&quot;
 },
 &quot;statusPageUrl&quot;: &quot;http://192.168.119.1:8082/info&quot;,
 &quot;secureVipAddress&quot;: &quot;springcloud-deep-provider&quot;,
 &quot;leaseInfo&quot;: {
 &quot;renewalIntervalInSecs&quot;: 30,
 &quot;registrationTimestamp&quot;: 1537840715486,
 &quot;evictionTimestamp&quot;: 0,
 &quot;renewalTimestamp&quot;: 1537840715486,
 &quot;durationInSecs&quot;: 90,
 &quot;serviceUpTimestamp&quot;: 1537840715486
 },
 &quot;homePageUrl&quot;: &quot;http://192.168.119.1:8082/&quot;,
 &quot;countryId&quot;: 1,
 &quot;sID&quot;: &quot;na&quot;,
 &quot;securePort&quot;: 443,
 &quot;dataCenterInfo&quot;: {
 &quot;name&quot;: &quot;MyOwn&quot;
 },
 &quot;instanceId&quot;: &quot;localhost:springcloud-deep-provider:8082&quot;,
 &quot;coordinatingDiscoveryServer&quot;: false,
 &quot;id&quot;: &quot;localhost:springcloud-deep-provider:8082&quot;,
 &quot;vIPAddress&quot;: &quot;springcloud-deep-provider&quot;,
 &quot;dirty&quot;: false,
 &quot;lastUpdatedTimestamp&quot;: 1537840715486,
 &quot;healthCheckUrl&quot;: &quot;http://192.168.119.1:8082/health&quot;,
 &quot;appName&quot;: &quot;SPRINGCLOUD-DEEP-PROVIDER&quot;,
 &quot;lastDirtyTimestamp&quot;: 1537840715451,
 &quot;iPAddr&quot;: &quot;192.168.119.1&quot;,
 &quot;version&quot;: &quot;unknown&quot;,
 &quot;actionType&quot;: &quot;ADDED&quot;,
 &quot;port&quot;: 8082,
 &quot;healthCheckUrls&quot;: [
 &quot;http://192.168.119.1:8082/health&quot;
 ],
 &quot;status&quot;: &quot;UP&quot;
 }
 ]
 }

  • 至此,本次实战就完成了,通过开发自定义的广播监听器,我们对Eureka的注册发现机制有了进一步了解,在您的Spring Cloud学习过程中,希望本文能祝您一臂之力;

欢迎关注InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...
原网站

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://xie.infoq.cn/article/c717c9087514fbbb31854988f