苏渡苇头像
关注
Spring Insight中的监控链路如何画出服务拓扑封面图

Spring Insight中的监控链路如何画出服务拓扑

作者:苏渡苇

项目地址https://github.com/iweidujiang/spring-insight(感谢 Star !)

本文要讲什么

上一篇把收集进来的 Span 洗完了:缺的服务名、耗时、状态会补上,然后才交给存储。

控制台还要画一张 谁调用了谁 的图,箭头不是前端编的,也不是再扫一遍 SERVER Span 猜出来的,用的是出站记录上的 remoteService

进站拦截器只知道自己被谁访问了,不知道自己又去打了哪个下游;拓扑要的是调用边,得靠 Feign、Gateway 这类出站埋点把远端服务名写上。

这篇要讲的就是这条边:remoteService 在采集侧怎么填,Server 怎么聚成 source → target,以及为什么只有带远端名的 Span 才会变成箭头。

一、先把问题说清楚

一张拓扑图需要节点和边。节点是服务名,边是 A 调用了 B,还要带上次数和平均耗时。

HTTP 进站 Span 的 serviceName 是自己,remoteService 通常是空的,拿它画图只会得到一堆孤立点。

真正能连线的,是 CLIENT Span:

Gateway  --Feign/转发-->  order  --Feign-->  user
                         --Feign-->  product

BeanPostProcessor 这篇文章中给 Feign Client 套了 TracingFeignClient,Gateway 侧还有 InsightGatewayTracingFilter,两处都会 setRemoteService,拓扑聚合只认这个字段。

聚合代码在此:TraceSpanPersistenceService.getServiceDependencies

查询接口:GET /api/v1/ui/dependencies?hours=24

二、采集侧把远端名写上

Feign 出站时,目标先从请求 URL 里取 host:

String remote = resolveRemoteService(url);
clientSpan.setSpanKind("CLIENT");
clientSpan.setComponent("OpenFeign");
clientSpan.setRemoteService(remote);
clientSpan.setRemoteEndpoint(path);

resolveRemoteService 做的是 URI.create(url).getHost(),解析失败就写成 unknown

走 Spring Cloud LoadBalancer 时,URL 往往是 http://sca-user/user/1 这种,host 就是服务名,图上的节点才能对上 spring.application.name。要是写成 IP 或机器名,图上就会出现一串地址,和旁边的逻辑服务名对不齐,这是当前实现,没有再做一层服务名映射。

Gateway 出站不走 Feign,看的是路由上的 URI:lb://sca-order 取 host 得到 sca-order;不是 lb 方案时退回路由 host,再不行用 GATEWAY_REQUEST_URL_ATTR 里的下游地址。

if ("lb".equalsIgnoreCase(uri.getScheme()) && uri.getHost() != null) {
    return uri.getHost();
}

两边都是 CLIENT Span,都带 remoteService,Server 聚合时不用区分 Feign 还是 Gateway,字段齐了就能连线。

三、Server 按边累加

getServiceDependencies(lastHours) 扫内存列表,只处理时间窗口内、并且 remoteService 非空的 Span:

String remote = s.getRemoteService();
if (remote == null || remote.isBlank()) {
    continue;
}
String src = s.getServiceName() != null ? s.getServiceName() : "";
Key k = new Key(src, remote);
long[] a = agg.computeIfAbsent(k, x -> new long[]{0L, 0L});
a[0]++;                 // 次数
a[1] += n(s.getDurationMs());  // 耗时总和

同一对 source_service + target_service 合成一条边,call_count 是次数,avg_duration 是耗时总和除以次数。没有索引,就是加锁后扫一遍,和上一篇之后的存储形态一致。

前端 ApiService.getServiceDependencies 会把下划线字段收成驼峰,再交给 buildTopologyOption 做 ECharts 的节点和 link。节点大小、线宽跟次数有关,布局用 circular,避免力导向把点甩出视口;这些是展示,边从哪来已经在 Server 定了。

时间窗口默认 24 小时,窗口外的 Span 不参与连线,存储淘汰掉的旧数据也不会再出现在图上。

四、和只看进站 Span 的区别

可以对照着看:

SERVER / 多数 JDBC SpanCLIENT Span(Feign / Gateway)
serviceName自己自己
remoteService通常空URL host 或 lb:// 服务名
对拓扑的贡献几乎没有边成为 source → target
适合回答的问题谁被访问了、耗时多少谁调用了谁

只靠进站埋点,最多列出服务清单;箭头必须来自出站。

unknown 也会变成一个节点。

URL 解析失败、路由还没解析出 host,图上就会多出一个名叫 unknown 的点,这不是下游真有这个服务,是远端名没取到。

五、小结

拓扑要回答的是 谁调用了谁,边只能来自带 remoteService 的 CLIENT Span:Feign 取 URL host,Gateway 优先取 lb:// 后面的服务名。Server 按 (serviceName, remoteService) 累加次数和耗时,接口是 GET /api/v1/ui/dependencies;前端只负责箭头和布局,不负责发现依赖。

进站 HTTP、多数 JDBC 记录不参与连线,图上没有库节点是当前实现,不是漏画。看图时还有几处容易误会:空远端不会连线;host 写成 IP 或机器名,节点就会和服务名对不齐;解析失败会出现名叫 unknown 的点,并不是下游真有这个服务;图空了不一定是没调用,也可能是窗口外,或数据已经被存储上限挤掉了。

图看的是服务之间的关系,链路页看的是一次请求经过了哪些 Span,两份数据都在同一份内存列表里,只是聚合方式不同:拓扑按远端服务名归边,列表按 traceId 归成一行。

下集预告:看链路列表怎么把一堆 Span 收成一行 Trace,以及根服务、总耗时、是否有错误是怎么从扁平记录里抠出来的。

最后:欢迎围观 Spring Insight

如果你对轻量监测、Spring Boot Starter、链路埋点感兴趣,欢迎看看这个还在打磨的小项目:

🔗 GitHubhttps://github.com/iweidujiang/spring-insight

当前形态很朴素:业务服务加 spring-insight-agent-starter,旁边单独跑 insight-server 看拓扑和链路。能力有限,代码也还糙,适合当练手和对照。

你的 Star 是对我最大的鼓励。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/iweidujiang/article/details/164853947

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--