Spring Boot Admin(SBA)核心流程-注册篇

SBA是什么

首先我们简单了解一下Spring Boot Admin(SBA),以下统一简称SBA是什么。借用官网的描述:

SBA 是 codecentric 公司开发的一款开源社区项目,目标是让用户更方便的管理以及监控 Spring Boot ® 应用。 应用可以通过我们的SBA客户端(通过HTTP的方式)或者使用Spring Cloud ®(比如Eureka,consul的方式)注册。 基于Spring Boot Actuator默认接口开发的。

简单了解了SBA是什么,我们就开始介绍SBA的核心流程,至于怎么搭建SBA客户端and服务端,可自行百度,这类的文章居多。建议阅读本文章之前,先搭建SBA使用。

SBA客户端如何注册到服务端

首先SBA分为客户端跟服务端两个部分,客户端为我们需要监控的应用,服务端则是可以看到各个应用的监控情况。需要实现这一点,首先我们需要将客户端注册到服务端。SBA有两种注册方式,一种通过http注册,一种通过注册中心比如Eureka,consul等。

客户端如何通过http的方式注册到服务端

我们先讲讲通过http注册的流程。

  1. 客户端首先需要引入SBA客户端的依赖,这里以2.7.10为例。客户端的依赖版本取决于你的应用的Spring Boot版本。我的应用版本是2.7.0,所以SBA客户端对应版本用的2.7.X。
xml 复制代码
<dependency>  
<groupId>de.codecentric</groupId>  
<artifactId>spring-boot-admin-starter-client</artifactId>  
<version>2.7.10</version>  
</dependency>

<!--actuator必须引入,监控数据来源于actuator-->
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-actuator</artifactId>  
</dependency>

2.配置SBA参数

yml 复制代码
#开启SBA配置
spring.boot.admin.client.enabled=true

#注册到服务端的url
spring.boot.admin.client.url=http://localhost:9001  
#让actuator端点先全部暴露出来,正式环境请酌情配置
management.endpoints.web.exposure.include=*

接下来我们就从源码的角度,讲讲客户端如何注册到服务端。 这块的入口代码在RegistrationApplicationListener类中

java 复制代码
@EventListener  
@Order(Ordered.LOWEST_PRECEDENCE)  
public void onApplicationReady(ApplicationReadyEvent event) {  
  if (autoRegister) {  
   startRegisterTask();  
  }  
}

从上面代码可以看出来在应用启动后,会执行一个定时任务。而这个定时任务就是客户端注册的任务。 核心代码如下:

java 复制代码
@Override  
public boolean register() {  
//构建一个Application实例,其中包括应用的名称、应用的healthUrl、managementUrl、serviceUrl等。
Application application = this.applicationFactory.createApplication();  
boolean isRegistrationSuccessful = false;  
//这个adminUrls就是客户端配置的spring.boot.admin.client.url的值。可以用逗号分割,配置多个。
for (String adminUrl : this.adminUrls) {  
 LongAdder attempt = this.attempts.computeIfAbsent(adminUrl, (k) -> new LongAdder());  
 //核心注册方法,会通过HTTP的方法发送post请求到你配置的spring.boot.admin.client.url进行注册,
 //请求体就是构建的Application实例。
 boolean successful = register(application, adminUrl, attempt.intValue() == 0);  
  
if (!successful) {  
 attempt.increment();  
}  
else {  
 attempt.reset();  
 isRegistrationSuccessful = true;  
if (this.registerOnce) {  
 break;  
}  
}  
}  
  
return isRegistrationSuccessful;  
}

简而言之客户端启动成功后,会定时向服务端发起注册请求。服务端的url需要进行客户端进行配置,这个定时任务默认是10秒一次。

这个注册方式比较简单粗暴,个人感觉不如注册中心的方法好使。核心代码也比较简单。感兴趣的朋友可以自己阅读一下源码。

客户端如何通过服务发现的方式注册到服务端

重点讲解一下SBA客户端如何通过服务发现的方式注册到服务端。 本次讲解以Eureka为例。

  1. 启动一个Eureka
  2. 客户端依旧需要引入SBA client的依赖,参考http方式注册。
  3. 服务端跟客户端都要添加Eureka配置
yml 复制代码
eureka:  
  client:  
    register-with-eureka: true  
    fetch-registry: true  
    service-url:  
      defaultZone: http://localhost:8001/eureka/
 #测试用,先把端点全部放出来     
 management:  
  endpoints:  
    web:  
      exposure:  
        include: '*'     
     

通过服务发现的方式注册最大的不同就是,客户端不需要配置服务的url.只需要配置注册中心,就能自动注册到服务端。下面我们从源码的角度讲解,SBA是如何实现的。

服务发现的核心代码在spring-boot-admin-server-cloud模块。核心逻辑主要是InstanceDiscoveryListener这个类。

  1. 服务端在启动的时候会创建InstanceDiscoveryListener这个类
java 复制代码
        @Bean  
        @ConditionalOnMissingBean  
        @ConfigurationProperties(prefix = "spring.boot.admin.discovery")  
        public InstanceDiscoveryListener instanceDiscoveryListener(ServiceInstanceConverter serviceInstanceConverter,  
                        DiscoveryClient discoveryClient, InstanceRegistry registry, InstanceRepository repository) {  
                InstanceDiscoveryListener listener = new InstanceDiscoveryListener(discoveryClient, registry, repository);  
                listener.setConverter(serviceInstanceConverter);  
                return listener;  
        }

2.InstanceDiscoveryListener类中监听了心跳事件

java 复制代码
@EventListener  
public void onParentHeartbeat(ParentHeartbeatEvent event) {  
       discoverIfNeeded(event.getValue());  
}

心跳的核心代码:

java 复制代码
protected void discover() {  
    Flux.fromIterable(discoveryClient.getServices()).filter(this::shouldRegisterService)  
    .flatMapIterable(discoveryClient::getInstances).filter(this::shouldRegisterInstanceBasedOnMetadata)  
    .flatMap(this::registerInstance).collect(Collectors.toSet()).flatMap(this::removeStaleInstances)  
    .subscribe((v) -> {  
    }, (ex) -> log.error("Unexpected error.", ex));  
}

简单解释一下这段代码的意思

1、从 discoveryClient获取服务列表。注册到eureka的服务都获取到。

2、用 shouldRegisterService 方法来过滤服务列表中的一些服务,保留应该注册的服务。

3、对每个满足条件的实例,执行 registerInstance 方法进行注册。

注册的核心逻辑就在registerInstance 方法中。接下来我们从该类接着看

java 复制代码
protected Mono<InstanceId> registerInstance(ServiceInstance instance) {  
    try {  
        // 拿到ServiceInstance转换成Registration进行注册。Registration包含应用name、managementUrl、serviceUrl等
        Registration registration = converter.convert(instance).toBuilder().source(SOURCE).build();  
        log.debug("Registering discovered instance {}", registration);  
        //进行注册
        return registry.register(registration);  
    }  
    catch (Exception ex) {  
        log.error("Couldn't register instance for discovered instance ({})", toString(instance), ex);  
        return Mono.empty();  
    }  
}

后面的注册逻辑跟通过http注册的逻辑就是相同的了。接下来我们看看共同的逻辑处理,服务端如何处理这些注册的应用。

SBA服务端如何处理注册的应用

不管是通过HTTP还是服务发现的方式注册到服务端,最后服务端处理注册的核心逻辑都是InstanceRegistry类。注册的核心代码如下:

java 复制代码
public Mono<InstanceId> register(Registration registration) {  
    Assert.notNull(registration, "'registration' must not be null");  
    //生成一个实例ID,同一个实例ID总是相同的。
    InstanceId id = generator.generateId(registration);  
    Assert.notNull(id, "'id' must not be null");  
    //这是重点
    return repository.compute(id, (key, instance) -> {  
    if (instance == null) {  
        instance = Instance.create(key);  
    }  
    return Mono.just(instance.register(registration));  
    }).map(Instance::getId);  
}

compute方法是更新或创建实例。它首先尝试查找实例,如果找到了就更新实例,如果没有找到就创建新实例。然后,将更新或新创建的实例传递给 save 方法进行保存。

SBA的实例默认都是保存在内存中的,SBA服务端通过保存的实例信息,每间隔一段时间(可配置)会发送http请求去请求健康状态、端点信息等。如果这个文章数据不错的话,后续会考虑更新一下相关健康状态、端点信息相关源码。

相关对标产品MOSS也不错,有兴趣的同学可以去了解一下。MOSS扩展了更多的端点,使监控数据更加直观。缺点就是很久没有维护了。拿来学习学习还是不错的。 附一个MOSS的链接: github.com/SpringCloud...

相关推荐
假装我不帅1 小时前
asp.net framework从webform开始创建mvc项目
后端·asp.net·mvc
神仙别闹1 小时前
基于ASP.NET+SQL Server实现简单小说网站(包括PC版本和移动版本)
后端·asp.net
计算机-秋大田1 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
货拉拉技术2 小时前
货拉拉-实时对账系统(算盘平台)
后端
掘金酱2 小时前
✍【瓜分额外奖金】11月金石计划附加挑战赛-活动命题发布
人工智能·后端
代码之光_19802 小时前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
ajsbxi3 小时前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
颜淡慕潇3 小时前
【K8S问题系列 |1 】Kubernetes 中 NodePort 类型的 Service 无法访问【已解决】
后端·云原生·容器·kubernetes·问题解决
戴眼镜的猴4 小时前
Spring Boot的过滤器与拦截器的区别
spring boot