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。
<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参数

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

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

@EventListener  
@Order(Ordered.LOWEST_PRECEDENCE)  
public void onApplicationReady(ApplicationReadyEvent event) {  if (autoRegister) {  startRegisterTask();  }  
}

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

@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配置
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这个类
        @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类中监听了心跳事件

@EventListener  
public void onParentHeartbeat(ParentHeartbeatEvent event) {  discoverIfNeeded(event.getValue());  
}

心跳的核心代码:

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 方法中。接下来我们从该类接着看

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类。注册的核心代码如下:

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的链接:
https://github.com/SpringCloud/Moss

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/175231.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

011 OpenCV warpAffine

目录 一、环境 二、warpAffine原理 三、完整代码 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、warpAffine原理 warpAffine是OpenCV库中的一个函数&#xff0c;它用于执行二维仿射变换。这个函数接受一个输入图像和变换矩阵&…

二叉树的递归套路(1)

与其明天开始&#xff0c;不如现在行动&#xff01; 文章目录 是否平衡二叉树 &#x1f48e;总结 是否平衡二叉树 题目 给定一颗二叉树的头节点head&#xff0c;返回这颗二叉树是不是平衡二叉树 平衡二叉树就是这个树的所有子树和它自己&#xff0c;左右子树高度差不超过1 递归…

Rust语言入门教程(六) - 字符串类型

在Rust中&#xff0c; 字符串类型其实是一个比较复杂的话题。在Rust的标准库中&#xff0c;至少都提供了6种字符串类型&#xff0c;我们平常使用的最多的是其中的两种。这两种类型互相之间也有所关联&#xff1a; str&#xff1a; 字符串切片String 字符串 其中&#xff0c; 字…

Postman接口测试 —— 设置断言和集合运行

一、常见的5种断言方法 Postman是一款非常强大的API接口调式工具&#xff0c;它自带断言方法&#xff0c;不需要学习JavaScript脚本&#xff0c;非常方便。 &#xff08;1&#xff09;Status code&#xff1a;Code is 200(校验接口返回结果的状态码) &#xff08;2&#xff09…

python+feon有限元分析|求解实例

目录 1、feon框架结构 2. 支持的单元类型 3、实例 1、feon框架结构 包含三个包&#xff1a; sa&#xff1a;结构分析包 ffa&#xff1a;流体分析包 derivation&#xff1a;刚度矩阵包 2. 支持的单元类型 Spring1D11 - 一维弹簧单元 Spring2D11 - 二维弹簧单元 Spring…

Java进行计算两个时间间隔

在Java中&#xff0c;我们经常需要计算两个时间之间的间隔&#xff0c;比如计算某个任务的执行时间、计算两个事件发生的时间间隔等等。Java提供了一些类和方法来处理时间和日期相关的操作&#xff0c;本文将介绍如何使用Java来计算两个时间之间的间隔&#xff0c;并提供相应的…

再探Docker:从Docker基础到跨服务器部署

摘要&#xff1a; 这篇文章将从介绍Docker基础开始&#xff0c;逐步讲解如何创建镜像、使用Docker Compose编排容器、在Docker中更新部署环境&#xff0c;将更新后的环境打包为镜像并导出为tar包&#xff0c;最后在其他服务器上应用这个镜像。 1. Docker是什么 Docker是一种容…

MySQL运行在docker容器中会损失多少性能

前言 自从使用docker以来&#xff0c;就经常听说MySQL数据库最好别运行在容器中&#xff0c;性能会损失很多。一些之前没使用过容器的同事&#xff0c;对数据库运行在容器中也是忌讳莫深&#xff0c;甚至只要数据库跑在容器中出现性能问题时&#xff0c;首先就把问题推到容器上…

Java学习路线第一篇:Java基础(2)

这篇则分享Java学习路线第一part&#xff1a;Java基础&#xff08;2&#xff09; 从看到这篇内容开始&#xff0c;你就是被选定的天命骚年&#xff0c;将承担起学完Java基础的使命&#xff0c;本使命为单向契约&#xff0c;你可选择YES或者选择YES。 具体路线安排&#xff1a…

ubuntu 下载编译 opencv4.2.0并检验

如有帮助点赞收藏关注&#xff01; 如需转载&#xff0c;请注明出处&#xff01; ubuntu 的opencv4.2.0下载与编译 下载依赖开始编译安装配置OpenCV编译环境检验* 完成 下载 首先下载opencv源码网址&#xff1a; https://opencv.org/releases/page/3/ 下载成zip后&#xff0c;…

QMap key()和value(const Key key, const T defaultValue = T()) const第二个参数作用

value()函数介绍 返回与键key关联的值。 如果map不包含键为key的项&#xff0c;则该函数返回defaultValue。 如果没有指定defaultValue&#xff0c;该函数返回一个默认构造的值。 如果映射中有多个key项&#xff0c;则返回最近插入的项的值。 示例 定义自定义类型&#xff…

vue-历史模式部署

项目部署 本项目采用nginx进行部署&#xff0c;历史模式的部署需要服务端的配合&#xff0c;本次采用nginx进行配合。 1 配置 const basePath process.env.VUE_APP_BASE_PATH; module.exports {publicPath: basePath #静态资源的路径 /ecology/ }2 创建路由 const createR…

3D模型顶点颜色转纹理【SIMPLYGON】

在这篇博客中&#xff0c;我们将了解如何将顶点颜色烘焙到纹理中。 其用例是某些照片扫描资产&#xff0c;其中颜色数据保存到顶点颜色中。 我们将了解如何使用 remesher 和聚合器管道来完成此操作。 我们还将介绍如何为顶点颜色材质创建着色网络以及如何从模型后处理中删除顶点…

6.Spring源码解析-loadBeanDefinitions(String location)

这里resourceLoader其实就是ClassPathXmlApplicationContext 1.ClassPathXmlApplicationContext 在上文中图例就能看出来 获取资源组可能存在多个bean.xml 循环单独加载资源组 创建一个编码资源并解析 获取当前正在加载的资源发现是空 创建了一个字节输入流&#xff0c…

pytorch分布式训练

1 基本概念 rank&#xff1a;进程号&#xff0c;在多进程上下文中&#xff0c;我们通常假定rank 0是第一个进程或者主进程&#xff0c;其它进程分别具有1&#xff0c;2&#xff0c;3不同rank号&#xff0c;这样总共具有4个进程 node&#xff1a;物理节点&#xff0c;可以是一个…

Find My键盘|苹果Find My技术与键盘结合,智能防丢,全球定位

键盘是最常用也是最主要的输入设备&#xff0c;通过键盘可以将英文字母、汉字、数字、标点符号等输入到计算机中&#xff0c;从而向计算机发出命令、输入数据等。还有一些带有各种快捷键的键盘。随着时间的推移&#xff0c;渐渐的市场上也出现独立的具有各种快捷功能的产品单独…

单例模式-C++实现

目录 饿汉式懒汉式双检查锁&#xff0c;线程安全的版本什么是reorder&#xff1f;解决内存读写reorder不安全方法代码解释懒汉式的优缺点 单例模式是一种设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局的访问点来获取该实例。它常用于需要在整个应…

【软考】模块的内聚类型

目录 一、偶然内聚二、逻辑内聚三、时间内聚四、过程内聚五、通信内聚六、顺序内聚七、功能内聚 一、偶然内聚 1.最弱的内聚类型 2.又称巧合内聚&#xff0c;模块的各个成分之间毫无关系 二、逻辑内聚 1.逻辑上相关的功能被放在同一个模块中 2.如一个模块读取各种不同类型外设…

QTextEdit 是 Qt 框架中的一个类,用于显示和编辑多行文本内容的可编辑部件

QTextEdit 是 Qt 框架中的一个类&#xff0c;用于显示和编辑多行文本内容的可编辑部件。 QTextEdit 提供了一个用于显示和编辑富文本&#xff08;包括格式化文本、图像和链接等&#xff09;和纯文本的文本编辑器。它支持基本的文本操作&#xff08;如复制、粘贴、撤销、重做等…

自己动手实现一个深度学习算法——八、深度学习

深度学习是加深了层的深度神经网络。 1.加深网络 1&#xff09;向更深的网络出发 创建一个如下图所示的网络结构的CNN 这个网络的层比之前实现的网络都更深。这里使用的卷积层全都是33 的小型滤波器&#xff0c;特点是随着层的加深&#xff0c;通道数变大&#xff08;卷积…