spring cloud alibaba 应用无法注册到sentinel dashboard
一。技术背景
由于升级jdk17的需要 我们将项目中的 spring cloud spring cloud alibaba 以及springboot进行了升级 各版本如下
spring cloud 2021.0.5
spring cloud alibaba 2021.0.5.0
spring boot 2.6.13
二。问题表现
当启动项目服务后,服务无法注册到 sentinel-dashboard
三。错误排查
- 首先检查sentinel-dashboard 启动状态 启动成功并且可以正常访问且不存在网络问题
- 环境配置检查
<!-- 依赖检查 无误 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
配置检查
#配置也正常
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.transport.port=8719
- 第三步源码追踪
接下来开始漫长源码分析步骤
然后点击 spring.cloud.sentinel.transport.dashboard 这条配置 跳转 com.alibaba.cloud.sentinel.SentinelProperties.Transport#setDashboard
然后点击 getDashboard() 方法查看在哪里调用 最后来到了 com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration#init
在如下代码中
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
@EnableConfigurationProperties(SentinelProperties.class)
public class SentinelAutoConfiguration {@PostConstructprivate void init() {///省略部分逻辑 if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))&& StringUtils.isNotBlank(properties.getTransport().getPort())) {System.setProperty(TransportConfig.SERVER_PORT,properties.getTransport().getPort());}if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER))&& StringUtils.isNotBlank(properties.getTransport().getDashboard())) {System.setProperty(TransportConfig.CONSOLE_SERVER,properties.getTransport().getDashboard());}}
}
断点时 发现配置成功被设置到 系统的属性配置中
接下来再来看 心跳发送具体类 HeartbeatSenderInitFunc
com.alibaba.csp.sentinel.transport.init.HeartbeatSenderInitFunc#init
@Override
public void init() {HeartbeatSender sender = HeartbeatSenderProvider.getHeartbeatSender();if (sender == null) {RecordLog.warn("[HeartbeatSenderInitFunc] WARN: No HeartbeatSender loaded");return;}initSchedulerIfNeeded();long interval = retrieveInterval(sender);setIntervalIfNotExists(interval);//定时调度发送心跳scheduleHeartbeatTask(sender, interval);
}
具体得调度逻辑 每5秒发送一次心跳
private void scheduleHeartbeatTask(/*@NonNull*/ final HeartbeatSender sender, /*@Valid*/ long interval) {pool.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {sender.sendHeartbeat();} catch (Throwable e) {RecordLog.warn("[HeartbeatSender] Send heartbeat error", e);}}}, 5000, interval, TimeUnit.MILLISECONDS);RecordLog.info("[HeartbeatSenderInit] HeartbeatSender started: "+ sender.getClass().getCanonicalName());
}
我们再来看下具体的实现
sender.sendHeartbeat();
实现类只有一个 SimpleHttpHeartbeatSender
com.alibaba.csp.sentinel.transport.heartbeat.SimpleHttpHeartbeatSender#sendHeartbeat
@Override
public boolean sendHeartbeat() throws Exception { if (TransportConfig.getRuntimePort() <= 0) { RecordLog.info("[SimpleHttpHeartbeatSender] Command server port not initialized, won't send heartbeat"return false; } Endpoint addrInfo = getAvailableAddress(); if (addrInfo == null) { return false; } SimpleHttpRequest request = new SimpleHttpRequest(addrInfo, TransportConfig.getHeartbeatApiPath()); request.setParams(heartBeat.generateCurrentMessage()); try { SimpleHttpResponse response = httpClient.post(request); if (response.getStatusCode() == OK_STATUS) { return true; } else if (clientErrorCode(response.getStatusCode()) || serverErrorCode(response.getStatusCode())) { RecordLog.warn("[SimpleHttpHeartbeatSender] Failed to send heartbeat to " + addrInfo + ", http status code: " + response.getStatusCode()); } } catch (Exception e) { RecordLog.warn("[SimpleHttpHeartbeatSender] Failed to send heartbeat to " + addrInfo, e); } return false;
}
以上就是发送心跳的逻辑
核心逻辑
- 获取有效的链接
- 创建连接发送心跳请求
- 响应以及异常处理
但是在断点过程中 有效的链接列表居然是空的 这就是导致代码无法继续向下

然后我们继续围绕这个点进行排查
获取有效的地址列表方法如下
private Endpoint getAvailableAddress() {if (addressList == null || addressList.isEmpty()) {return null;}if (currentAddressIdx < 0) {currentAddressIdx = 0;}int index = currentAddressIdx % addressList.size();return addressList.get(index);
}
发现使用的成员变量 addressList 我们再来找下这个值的赋值操作
赋值操作只有一个地方 在SimpleHttpHeartbeatSender的构造方法中
public SimpleHttpHeartbeatSender() {// Retrieve the list of default addresses.List<Endpoint> newAddrs = TransportConfig.getConsoleServerList();if (newAddrs.isEmpty()) {RecordLog.warn("[SimpleHttpHeartbeatSender] Dashboard server address not configured or not available");} else {RecordLog.info("[SimpleHttpHeartbeatSender] Default console address list retrieved: {}", newAddrs);}this.addressList = newAddrs;
}
com.alibaba.csp.sentinel.transport.config.TransportConfig#getConsoleServerList
public static List<Endpoint> getConsoleServerList() {String config = SentinelConfig.getConfig(CONSOLE_SERVER);List<Endpoint> list = new ArrayList<Endpoint>();if (StringUtil.isBlank(config)) {return list;}//。。。。。省略部分return list;
}
com.alibaba.csp.sentinel.config.SentinelConfig#getConfig(java.lang.String)
public static String getConfig(String key) {AssertUtil.notNull(key, "key cannot be null");return props.get(key);
}
我们再来看下 props的初始化
在SentinelConfig的静态构造中
static {try {initialize();//加载配置loadProps();resolveAppName();resolveAppType();RecordLog.info("[SentinelConfig] Application type resolved: {}", appType);} catch (Throwable ex) {RecordLog.warn("[SentinelConfig] Failed to initialize", ex);ex.printStackTrace();}
}
com.alibaba.csp.sentinel.config.SentinelConfig#loadProps
private static void loadProps() {///从配置加载中获取配置Properties properties = SentinelConfigLoader.getProperties();for (Object key : properties.keySet()) {setConfig((String) key, (String) properties.get(key));}
}
从SentinelConfigLoader 获取到配置
public static Properties getProperties() {return properties;
}
而这个properties的初始化是在SentinelConfigLoader 静态构造方法中
static {try {load();} catch (Throwable t) {RecordLog.warn("[SentinelConfigLoader] Failed to initialize configuration items", t);}
}
private static void load() {// Order: system property -> system env -> default file (classpath:sentinel.properties) -> legacy pathString fileName = System.getProperty(SENTINEL_CONFIG_PROPERTY_KEY);if (StringUtil.isBlank(fileName)) {fileName = System.getenv(SENTINEL_CONFIG_ENV_KEY);if (StringUtil.isBlank(fileName)) {fileName = DEFAULT_SENTINEL_CONFIG_FILE;}}Properties p = ConfigUtil.loadProperties(fileName);if (p != null && !p.isEmpty()) {RecordLog.info("[SentinelConfigLoader] Loading Sentinel config from {}", fileName);properties.putAll(p);}for (Map.Entry<Object, Object> entry : new CopyOnWriteArraySet<>(System.getProperties().entrySet())) {String configKey = entry.getKey().toString();String newConfigValue = entry.getValue().toString();String oldConfigValue = properties.getProperty(configKey);properties.put(configKey, newConfigValue);if (oldConfigValue != null) {RecordLog.info("[SentinelConfigLoader] JVM parameter overrides {}: {} -> {}",configKey, oldConfigValue, newConfigValue);}}
}
看到这里就大概明白了,因为这是静态代码话 肯定优先初始化,而我们的地址在项目启动bean加载中才会设置到System的Properties里
所以获取的 地址一直是空的。 也无法注册到sentinel-dashboard上
四。解决办法
- idea 启动类 增加参数 指定dashboard server地址 以及应用名称
-Dcsp.sentinel.dashboard.server=localhost:8080 -Dcsp.sentinel.app.name=gateway-service
- 启动类设置系统变量
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayServiceApplication {public static void main(String[] args) {System.setProperty("csp.sentinel.dashboard.server","localhost:8080");System.setProperty("csp.sentinel.app.name","gateway-service");SpringApplication.run(GatewayServiceApplication.class, args);}}
五。后续分析旧的版本的依赖对应的实现方式
旧的依赖版本为
springboot 2.3.12.RELEASE
spring cloud Hoxton.SR12
spring cloud alibaba 2.2.9.RELEASE
相关文章:
spring cloud alibaba 应用无法注册到sentinel dashboard
一。技术背景 由于升级jdk17的需要 我们将项目中的 spring cloud spring cloud alibaba 以及springboot进行了升级 各版本如下 spring cloud 2021.0.5 spring cloud alibaba 2021.0.5.0 spring boot 2.6.13 二。问题表现 当启动项目服务后,服务无法注册到 sentin…...
如何在vue3中加入markdown语法
1、首先需要安装 md-editor-v3 yarn add md-editor-v3 或者是在vue图形化界面中直接搜索 md-editor-v3 进行安装。 2、引入该编辑页 引入可以参考这个,根据自己的需求进行修改和添加。 <template><md-editor v-model"text"/> </templat…...
R语言的物种气候生态位动态量化与分布特征模拟实践技术
在全球气候快速变化的背景下,理解并预测生物种群如何应对气候变化,特别是它们的地理分布如何变化,已经变得至关重要。利用R语言进行物种气候生态位动态量化与分布特征模拟,不仅可以量化描述物种对环境的需求和适应性,预…...
大数据Flink(六十一):Flink流处理程序流程和项目准备
文章目录 Flink流处理程序流程和项目准备 一、Flink流处理程序的一般流程...
C语言快速回顾(一)
前言 在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》,结合我自己的工作学习经历,我准备写一个音视频系列blog。C/C是音视频必…...
Element Plus报错:ResizeObserver loop completed with undelivered notifications.
el-selected踩坑:el-selected 显示下拉框 mouseover 时报错!!! 原来是属性 popper-append-to-body 被废除,改为 teleported。 element ui <el-select:popper-append-to-body"false"value-key"id&q…...
scope穿透(二)
上篇文章已经讲了,如何穿透样式,今天我们进入element-ui官网进行大规模的穿透处理。 1.输入框 <template><div class""><el-input v-model"input" placeholder"请输入内容"></el-input></div> </template>…...
2023+HuggingGPT: Solving AI Tasks with ChatGPT and itsFriends in Hugging Face
摘要: 语言是llm(例如ChatGPT)连接众多AI模型(例如hugs Face)的接口,用于解决复杂的AI任务。在这个概念中,llms作为一个控制器,管理和组织专家模型的合作。LLM首先根据用户请求规划任务列表,然后为每个任务分配专家模…...
Ant Design Mobile是什么?
在当今的数字时代,移动应用程序和网页设计已经成为各行各业的重要组成部分。用户界面的设计直接影响到用户体验和产品的成功。为了帮助设计师在移动设计领域更好,Antdesignmobile应运而生。Antdesignmobile是蚂蚁金服的移动UI设计语言和框架,…...
深入理解设计模式-行为型之模板(和回调区别联系)
概述 模板设计模式(Template Design Pattern)是一种行为型设计模式,它定义了一个算法的骨架,将算法的一些步骤延迟到子类中实现。模板设计模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。 模板设计模式的核心思想…...
LabVIEW控制通用工作台
LabVIEW控制通用工作台 用于教育目的的计算机化实验室显着增长,特别是用于运动控制的实验室。它们代表了各种工业应用中不断扩大的领域,并成为以安全的方式使用通常昂贵或独特的实验室设备进行实时实验的宝贵工具。NI LabVIEW等软件应用程序的开发和不断…...
什么是事务,并发带来的事务问题以及事务隔离级别(图文详解)
一、什么是事务? 简单说就是逻辑上的一组操作,要么都执行,要么都不执行。 举个例子,假如小明要给小红转账100元,这个转账会涉及到两个关键操作:①将小明的余额减少100元。 ②将小红的余额增加100元 。但…...
【MySQL】MySQL数据库的delete from table和truncate table之间的区别
DELETE FROM table 和 TRUNCATE TABLE 是两种不同的数据库操作,用于从MySQL数据库的表中删除数据。它们有以下区别: 操作方式:DELETE FROM table 是一种逐行删除的操作,它会逐个删除表中的每一行数据,并且可以带有条件…...
强制Edge或Chrome使用独立显卡【WIN10】
现代浏览器通常将图形密集型任务卸载到 GPU,以改善你的网页浏览体验,从而释放 CPU 资源用于其他任务。 如果你的系统有多个 GPU,Windows 10 可以自动决定最适合 Microsoft Edge 自动使用的 GPU,但这并不一定意味着最强大的 GPU。 …...
easyx图形库基础:3实现弹球小游戏
实现弹球小游戏 一.实现弹球小游戏:1.初始化布:2.初始化一个球的信息:3.球的移动和碰撞反弹4.底边挡板的绘制和移动碰撞重置数据。 二.整体代码: 一.实现弹球小游戏: 1.初始化布: int main() {initgraph(800, 600);setorigin(40…...
vue基础知识三:v-show和v-if有什么区别?使用场景分别是什么?
一、v-show与v-if的共同点 我们都知道在 vue 中 v-show 与 v-if 的作用效果是相同的(不含v-else),都能控制元素在页面是否显示 在用法上也是相同的 <Model v-show"isShow" /> <Model v-if"isShow" />当表达式为true的时候&#…...
Python Opencv实践 - 图像旋转
import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR)#图像旋转 #Opencv中的旋转,首先通过cv.getRotationMatrix2D获得旋转矩阵 #cv.getRotationMatrix2D(center,ang…...
第五章 Opencv图像处理框架实战 5-10 文档扫描OCR识别
一、整体流程演示 上一篇我们进行了银行卡数字识别,这次我们利用opnecv等基础图像处理方法实现文档扫描OCR识别,该项目可以对任何一个文档,识别扫描出该文档上所有的文字信息。 为了方便后续程序运行,大家可以在Run->Edit Configuration中配置相关参数,选择相应编译器…...
CentOS 7 源码制作openssh 9.4p1 rpm包 —— 筑梦之路
参考之前的博客: centos 7 制作openssh8.7/8.8/8.9/9.0/9.1/9.2/9.3 p1 rpm包升级——筑梦之路_openssh rpm包_筑梦之路的博客-CSDN博客 需要说明的是9.4版本必须要openssl 1.1.1,低于此版本无法完成编译。这也是单独写这篇文章的必要性。 参考这篇编…...
OpenCV图像处理——轮廓检测
目录 图像的轮廓查找轮廓绘制轮廓 轮廓的特征轮廓面积轮廓周长轮廓近似凸包边界矩形最小外接圆椭圆拟合直线拟合 图像的矩特征矩的概念图像中的矩特征 图像的轮廓 查找轮廓 binary,contours,hierarchycv.findContours(img,mode,method)绘制轮廓 cv.drawContours(img,coutours…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10pip3.10) 一:前言二:安装编译依赖二:安装Python3.10三:安装PIP3.10四:安装Paddlepaddle基础框架4.1…...
算法—栈系列
一:删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...
