背景
我们最近发现一个问题,线上的应用在接入Sentinel后,在Sentinel Dashboard中能看到机器列表中的实例都处于健康状态,但是实时监控中却没有任何数据,因此为了排查问题,初步了解了Sentinel客户端与Dashboard之间是如何传输监控数据的。
Sentinel Dashboard
是一个独立的web服务,有一套可视化界面用于监控和管理其他接入Sentinel的应用,默认启动端口为8080。
一个应用如果要接入Sentinel,需接入sentinel-core
包,用于实现核心流控功能。
而想要接入Dashboard并与之通信则需要引入transport组件,Sentinel提供了两种选择:
sentinel-transport-simple-http
:基于jdk net包实现的,通过http协议传输数据sentinel-transport-netty-http
:在netty的基础上实现,通过http协议传输数据<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-transport-simple-httpartifactId>
<version>${sentinel.version}version>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-transport-netty-httpartifactId>
<version>${sentinel.version}version>
dependency>
这两个组件任选其一即可,它们的区别只在于通信协议的实现方式,功能没有区别,一般推荐使用sentinel-transport-netty-http
(下文以此为例)
只使用基本的流控功能的话,是不用额外指定启动参数的。但如果要接入Dashboard进行实时监控,则需要在应用启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=ip:port
指定控制台地址和端口。
其他相关参数有:
配置项 | 默认值 | 描述 |
---|---|---|
csp.sentinel.dashboard.server | localhost:8080 | dashboard服务的地址,用于传输通信 |
csp.sentinel.heartbeat.client.ip | 手动配置应用的ip,如无则自动获取 | |
csp.sentinel.api.port | 8719 | 应用用于传输数据的端口 |
project.name | 应用显示的名称 |
接入Sentinel并使用流控功能对相关资源请求进行了拦截处理后,资源请求的相关指标(qps、通过请求数、拒绝请求数等)会以两种形式保存在应用端:
ClusterBuilderSlot
中clusterNodeMap
保存了窗口时间内所有簇节点数据xx-metrics.log.${date}
的文件中记录下来以sentinel-transport-netty-http
为例,当Sentinel核心组件初始后,通过Spi扩展加载机制,HttpHeartbeatSender.java
会初始化一个定时任务,不断向csp.sentinel.dashboard.server
指定的Dashboard服务地址发送心跳信息。
此时应用实例信息即会出现在Dashboard界面的机器列表中。
同样基于Spi扩展加载机制,NettyHttpCommandCenter.java
会启动一个netty server,并监听csp.sentinel.api.port
配置的端口(默认8719),后面所有的监控数据都将通过此端口传输。
当通信服务启动成功后,会注册一系列CommandHandler,用于处理数据传输的请求,每个CommandHandler都与特定url绑定,相当于提供出去的接口。
所有的CommandHandler都在sentinel-transport-common
模块的com.alibaba.csp.sentinel.command.handler
包下:
CommandHandler对应的url和功能通过注解@CommandMapping
指定。
而主要用于返回监控指标数据的接口/metric
对应的是SendMetricCommandHandler
,在处理数据请求时,是通过MetricSearcher
类,去日志文件中查找相应时间范围的历史数据。
经过上述过程,应用已经与Dashboard服务建立了连接,Dashboard服务中,有一个名为MetricFetcher
的类,会不断获取各个应用实例的指标数据,方法调用:
MetricFetcher.fetchAllApp();
┆┈ MetricFetcher.doFetchAppMetric();
┆┈┈┈ MetricFetcher.fetchOnce();
而fetchOnce()
中其实就是组装了一个请求url,向http://ip:port/metric
接口发送http请求。
应用返回的指标数据,就会被存储到MetricsRepository
中:
MetricFetcher.writeMetric();
┆┈ MetricsRepository.saveAll();
应用的监控指标数据已经保存到Dashboard了,这个时候,我们在控制台界面来查看监控数据:
可以看到,页面会不断轮询Dashboard后端的/queryTopResourceMetric.json
接口,此接口由MetricController
提供:
MetricController.queryTopResourceMetric();
┆┈ MetricsRepository.queryByAppAndResourceBetween();
这里,就会去MetricsRepository
中查询之前保存的指标数据。
至此整个Sentinel客户端(应用)与控制台之间传输监控数据的流程就全部完成,总结一下,就是两部分内容:
希望这篇文章能给大家带来帮助,篇幅有限,很多细节的地方没有涉及到,建议可以通过Sentinel项目的源码深入学习,不管是对于问题排查,还是代码学习,都有很大的帮助。