springcloud之链路追踪

写在前面

源码 。
本文一起来看下链路追踪的功能,链路追踪是一种找出病因的手段,可以类比医院的检查仪器,服务医生治病救人,而链路追踪技术是辅助开发人员查找线上问题的。

1:为什么微服务需要链路追踪

孔子同志月过,有则改之,无则加勉,其中的后半句无则加勉,springcloud已经通过提供sentinel组件 ,但如何才能做到有则改之呢,想要改之,就必须知道要改啥,即要知道程序的bug是哪里造成的,而要定位问题的具体所在,在微服务场景下,有时候并不是一件那么容易的事情,因为可能涉及到非常多组件的调用,甚至还有消息队列,数据库之类的中间件,如下图:
在这里插入图片描述

组件多,并且一般多是多节点集群方式部署,用户的请求量还大,想要排查一个请求的错误,无异于大海捞针,因此啊,我们就需要一种技术来帮助我们将请求给串起来,这样在出现问题之后就可以拔出萝卜带出泥的定位到没有节点的请求日志,从而轻而易举的定位到问题所在,做到有则时的改之。而本文要分析的正是这样一个组件:sleuth。

那么sleuth是如何将每个请求串起来的呢?对于整个请求链路,sleuth定义了trace ID,对于一个请求定义了spanId,parentSpanId,其中spanId代表本次处理节点,parentSpanId代表上次处理节点,如下图:
在这里插入图片描述

有了调用链的日志之后,对于我们排查问题就会简单很多了,但是还是需要一个一个日志文件的来查找,其实还不是很方便,效率也比较低,此时如果能有一种图形化的展示方式,将这个跨进程的调用栈以UI的方式展示出来就太完美了,此时我们就需要zipkin组件的帮助了,所以应用程序sleuth需要将日志发送到zipkin供zipkin分析展示,但为了应用的解耦,我们一般会引入消息中间件,此时整个架构如下图:
在这里插入图片描述

接下来,进入实战。

2:实战

首先我们在模板,计算,用户三个模块添加依赖:

<!-- Sleuth依赖项 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

接着添加配置:

spring: sleuth:sampler:# 采样率的概率,100%采样probability: 1.0# 每秒采样数字最高为1000rate: 1000logging:level:...org.springframework.cloud.sleuth: debug
  • probability
    0到1的数字,设置采样的百分比。1为全部采样,0为全都不采样。
  • rate
    设置每秒最大采样数。
    请求,如果看到类似如下的日志就说明sleuth已经成功开始采集了:
DEBUG [coupon-customer-serv,69e433d6432522e4,936d8af942b703d2] 81584 
--- [io-20002-exec-1] c.g.c.customer.feign.TemplateService:xxxx

好,接着我们来搭建rabbitmq环境,具体不在这里赘述,可移步这里 查看。

接着,来搭建zipkin,首先从maven 的中央仓库 下载 zipkin-server-2.23.9-exec.jar,接着执行如下命令启动zipkin服务:

# 注意mq地址改成你自己的
java -jar zipkin-server-2.23.9-exec.jar --zipkin.collector.rabbitmq.addresses=localhost:5672

另外mq默认的信息如下:
在这里插入图片描述

如果需要指定的话,再启动命令中特殊指定即可,启动成功后会看到zipkin特有的logo:

$ java -jar zipkin-server-2.23.9-exec.jar --zipkin.collector.rabbitmq.addresses=192.168.10.79:5673ooooooooooooooooooooooooooooooooooooooooooooooooo  ooooooooooooo     ooooooooooooo       ooooooooooooo   o  o   oooooooooooo   oo  oo   ooooooooooooo  oooo  oooo  ooooooooooooo   ooooo  ooooo  ooooooooooooo   oooooo  oooooo  ooooooooooooooo      oo  oo      ooooooooooooooooooooo oo  oo ooooooooooooooooooooooooo  oooooooooooooooooooo  oooooooooooo  oooo________ ____  _  _____ _   _|__  /_ _|  _ \| |/ /_ _| \ | |/ / | || |_) | ' / | ||  \| |/ /_ | ||  __/| . \ | || |\  ||____|___|_|   |_|\_\___|_| \_|:: version 2.23.9 :: commit d6b1cc3 ::2024-01-16 17:19:11.706  INFO [/] 20860 --- [oss-http-*:9411] c.l.a.s.Server                           : Serving HTTP at /0:0:0:0:0:0:0:0:9411 - http://127.0.0.1:9411/

接着可以访问http://127.0.0.1:9411/:
在这里插入图片描述
zipkin会默认监听rabbitmq的队列zipkin来消费消息,所以此时你登录rabbitmq后台的话可以看到这个队列:
在这里插入图片描述

这样zipkin就搭建完成了。接着我们来改造服务,将采样信息生产到rabbitmq的zipkin队列中,首先在template,calculate,cusotm模块引入zipkin适配依赖,以及stream依赖完成生产消息的依赖:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<!-- 提前剧透Stream -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

接着添加配置:

spring:zipkin:sender:type: rabbitrabbitmq:addresses: 192.168.10.79:5673queue: zipkinrabbitmq:addresses: 192.168.10.79:5673

我们来几个请求后,就可以通过zipkin来查看了,会以栈的形式展现整个调用过程:
在这里插入图片描述
在这里插入图片描述
点击show就可以看到调用的整个过程了:
在这里插入图片描述
如果是某个服务调用报错的话,会显示为红色,如下:
在这里插入图片描述
如果知道traceId可以通过右上角直接搜索,如下:
在这里插入图片描述
zipkin也会根据服务调用关系生成依赖图,如下:
在这里插入图片描述

2.1:原理

首先我们打开spring.factories文件:
在这里插入图片描述
先看自动配置类TraceEnvironmentPostProcessor,这是一个EnvironmentPostProcessor,在该类中首先修改日志打印的格式,启动时会进入到这里:
在这里插入图片描述

在这里插入图片描述

这样日志格式就修改了,后续使用slf4j打印日志的话,只要有相关的信息,就会打印出来了了。接下来的问题就是spanId,tranceId等信息时什么时候设置的呢?继续看TraceWebAutoConfiguration自动配置类,源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(Tracer.class)
@ConditionalOnSleuthWeb
@Import({ SkipPatternConfiguration.class, TraceWebFluxConfiguration.class, TraceWebServletConfiguration.class })
@EnableConfigurationProperties(SleuthWebProperties.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceWebAutoConfiguration {}

继续看Import的TraceWebServletConfiguration,在该类中会创建TranceFilter,源码如下:

class TraceWebServletConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(value = "spring.sleuth.web.servlet.enabled", matchIfMissing = true)static class ServletConfiguration {...@Bean@ConditionalOnMissingBeanTracingFilter tracingFilter(CurrentTraceContext currentTraceContext, HttpServerHandler httpServerHandler) {return TracingFilter.create(currentTraceContext, httpServerHandler);}...}}

在TracingFilter会生成(第一个span时)或者时从header中获取traceId,spanId,等信息,并通过slf4j的MDC设置,这样,在打印日志的时候就会打印出这些信息了,源码如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {// 生成span,这里执行完traceId,spanId等就有值了Span span = handler.handleReceive(new HttpServletRequestWrapper(req));...// 设置到slf4j的MDC,这样打印日志就能获取到trance_id,span_id等信息了CurrentTraceContext.Scope scope = currentTraceContext.newScope(span.context());try {chain.doFilter(req, res);}...
}

那么故事的最后就是通过openfeign调用其他服务时是怎么实现拦截从而打印日志了,以RestTemplate方式调用为例,继续看spring.factories文件的TraceWebClientAutoConfiguration, 源码如下:

class TraceWebClientAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass(RestTemplate.class)static class RestTemplateConfig {...@Configuration(proxyBeanMethods = false)protected static class TraceInterceptorConfiguration {...@Beanstatic TraceRestTemplateBeanPostProcessor traceRestTemplateBeanPostProcessor(ListableBeanFactory beanFactory) {return new TraceRestTemplateBeanPostProcessor(beanFactory);}...}}...}

继续看TraceRestTemplateBeanPostProcessor这个后置bean处理器,最终执行如下方法,设置sleuth日志打印的拦截器:

// org.springframework.cloud.sleuth.instrument.web.client.TraceRestTemplateBeanPostProcessor#postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof RestTemplate) {RestTemplate rt = (RestTemplate) bean;// 设置拦截器new RestTemplateInterceptorInjector(interceptor()).inject(rt);}return bean;
}

这样在发起真正的日志调用之前就能打印日志了,其他的方式,如grpc,feign,也都是通过类似的方式来实现的,但不同的通信方式拦截的方式不同罢了,具体用到哪种再去研究吧!

3:elk

通过sleuth我们已经可以定位到异常发生在哪个模块了,并且可能也已经知道初步的异常信息了,但是仅仅如此还是没有办法定位到具体的异常原因,还需要详细的上下文日志信息,自然我们可以通过tranceId,spanId这些信息到具体的日志文件中去查找,但一般我们的实例都是多节点部署的,少则三四个,多则几十个上百个,所以人肉挨个查找日志文件的效率无疑会非常低。如果是能将所有的日志进行汇总,并提供一种简便的查询方式,就再也完美不过了,而elk ,就能很好的满足我们的需求。

首先来现在docker 的elk镜像 ,这里使用sebp/elk,如下命令:

# 比较大,耐心等待
docker pull sebp/elk:7.16.1
# 我这里设置为5G内存,小了容易不够用导致无法正常启动
docker run -p 5601:5601 -p 9200:9200 -p 5044:5044 -it --name elk --memory 5G sebp/elk:7.16.1

项目的日志文件,有错误可以通过日志文件排查问题:

==> /var/log/logstash/logstash-plain.log <====> /var/log/kibana/kibana5.log <====> /var/log/elasticsearch/elasticsearch.log <==

启动后可访问es:
在这里插入图片描述
kibana:
在这里插入图片描述
接着进入容器修改文件/etc/logstash/conf.d/02-beats-input.conf

docker exec -it elk /bin/bashinput {tcp {port => 5044codec => json_lines}
}output {elasticsearch {hosts => ["localhost:9200"]index => "geekbang"}
}

指定输入源和输出源,支持的详细输入源和输出源如下:
Logstash Input 插件列表 。

Logstash Output 插件列表 。

退出容器后,重启容器:

docker restart elk

接着我们来改造应用,支持将日志写到logstash中,首先在template,calcualte,custom三个模块中引入依赖,支持logback写数据到logstash:

<dependency><groupId>net.logstash.logback</groupId><artifactId>logstash-logback-encoder</artifactId><version>7.0.1</version>
</dependency>

接着添加两个appender,分别输出日志到控制台和logstash:

<!-- 控制台 --><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>DEBUG</level></filter><!-- 日志输出编码 --><encoder><pattern>${CONSOLE_LOG_PATTERN}</pattern><charset>utf8</charset></encoder></appender><!-- 输出的JSON格式的信息到Logstash --><appender name="logstash"class="net.logstash.logback.appender.LogstashTcpSocketAppender"><!-- 这是Logstash的连接方式 --><destination>192.168.10.79:5044</destination><!--<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder"></encoder>--><!-- 日志输出的JSON格式 --><encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"><providers><timestamp><timeZone>UTC</timeZone></timestamp><pattern><pattern>{"severity": "%level","service": "${applicationName:-}","trace": "%X{traceId:-}","span": "%X{spanId:-}","pid": "${PID:-}","thread": "%thread","class": "%logger{40}","rest": "%message"}</pattern></pattern></providers></encoder></appender>

通过appender logstash我们指定了json格式的数据。

  • 日志完整配置请参考源码。

接着我们来启动项目,发几个请求,这些数据就会被写到es中了,接着我们访问http://192.168.10.83:5601/需要首先创建index,大概操作如下:
在这里插入图片描述
接着点击如下位置:
在这里插入图片描述
一切顺利的话会进入如下页面:
在这里插入图片描述
接着选择一个时间段,refresh,就可以看到我们的日志信息了:
在这里插入图片描述
每一项就是es的每个文档了,其中的rest就是具体的日志信息,是我们在logback日志中设置的,可对比下图:
在这里插入图片描述
这样比如我们定位到了如下的调用链追踪日志:
在这里插入图片描述
可以定位到问题发生在template模块,并且指定知道是发生了java.lang.ArithmeticException,但为什么会出现这种情况就需要继续排查上下文的详细日志,此时我们就可以通过traceId:3cf4d5302905cd73到kibana中查看详细日志了:
在这里插入图片描述

写在后面

参考文章列表

Slf4j之MDC机制 。
spring cloud之集成sentinel 。

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

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

相关文章

中仕公考:国考进面后资格复审需要准备什么?

参加国考面试的考生在资格审核阶段需要准备以下材料&#xff1a; 1、本人身份证、学生证或工作证复印件。 2、公共科目笔试准考证复印件。 3、考试报名登记表。 4、本(专)科、研究生各阶段学历、学位证书(应届毕业生没有可以暂时不提供)。 5、报名资料上填写的各类证书材料…

前端:布局(用于div中有多行元素,一行只显示四个,最左或最右要紧贴父div,最顶层和最底层也要紧贴父div)

效果 一、flex实现 html <!DOCTYPE html> <html><head><title>Flexbox Layout</title><style>.container {display: flex;flex-wrap: wrap;justify-content: space-between;gap: 10px;border: 1px solid red;}.box {flex: 1 0 calc(25% …

2023年中国传感器行业研究报告(附传感器产业链全景图谱)

​传感器是一种通常由敏感元件和转换元件组成的检测装置&#xff0c;测量并感知信息后&#xff0c;通过变换让传感器中的数据或价值信息转换成电信号或其他所需形式的输出&#xff0c;以满足信息的传输、处理、存储、显示、记录和控制等要求。传感器是数字经济时代关键的硬件入…

Educational Codeforces Round 161 (Rated for Div. 2) B 2的零次方竟然是1

目录 心情&#xff1a; 55.999999999999993&#xff1a; 题意&#xff1a; 思路&#xff1a; 核心代码&#xff1a; 心情&#xff1a; Div.2&#xff0c;我竟然在50分钟内C题做掉了&#xff0c;想着B题做了基本上不会掉分了这把&#xff0c;B题要考什么我也清楚&#xff0…

【Kafka】Kafka介绍、架构和概念

目录 Kafka介绍Kafka优势Kafka应用场景Kafka基本架构和概念ProducerConsumer/Consumer GroupBrokerZooKeeperTopicPartitionReplicasOffsetsegment Kafka介绍 Kafka是是一个优秀的分布式消息中间件&#xff0c;关于常用的消息中间件对比可参考文章&#xff1a;消息中间件概述。…

【Docker】Nacos的单机部署及集群部署

一、Nacos的介绍 Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 动态服务发现&#xff1a;Nacos支持DNS与RPC服务发现&#xff0c;提供原生SDK、OpenAPI等多种服务注册方式和DNS、HTTP与API等多种服务发现方式。服务健康监测&#xff1a;Nacos提供…

Python数据分析案例32——财经新闻爬虫和可视化分析

案例背景 很多同学的课程作业都是需要自己爬虫数据然后进行分析&#xff0c;这里提供一个财经新闻的爬虫案例供学习。本案例的全部数据和代码获取可以参考&#xff1a;财经新闻数据 数据来源 新浪财经的新闻网&#xff0c;说实话&#xff0c;他这个网站做成这样就是用来爬虫的…

鸿蒙开发(五)鸿蒙UI开发概览

从用户角度来讲&#xff0c;一个软件拥有好看的UI&#xff0c;那是锦上添花的事情。再精确的算法&#xff0c;再厉害的策略&#xff0c;最终都得通过UI展现给用户并且跟用户交互。那么&#xff0c;本篇一起学习下鸿蒙开发UI基础知识&#xff0c;认识下各种基本控件以及使用方式…

Zoho Survey评价:功能全面,值得一试

通常来讲&#xff0c;我们在使用一款问卷调查制作工具制作问卷时会有哪些需求呢&#xff1f; 用户需求 1、操作简单&#xff0c;易上手。 2、能够满足用户个性化的需求。 3、提供多语言服务。 4、能够帮助发布以及数据收集。 5、简化数据分析 市面上的问卷调查制作工具都…

探索FTP:原理、实践与安全优化

引言 在正式开始讲解之前&#xff0c;首先来了解一下文件存储的类型有哪些。 DAS、SAN和NAS是三种不同的存储架构&#xff0c;分别用于解决不同场景下的数据存储需求。 DAS (Direct Attached Storage 直接附加存储)&#xff1a;DAS 是指将存储设备&#xff08;如硬盘&#x…

阿里云国外云服务器地域、收费标准及活动报价参考

阿里云国外服务器优惠活动「全球云服务器精选特惠」&#xff0c;国外服务器租用价格24元一个月起&#xff0c;免备案适合搭建网站&#xff0c;部署独立站等业务场景&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云国外服务器优惠活动&#xff1a; 全球云服务器精选特惠…

AI语音合成工具-Lalamu Studio

近期&#xff0c;Lalamu Studio开启了beta版本测试&#xff1a;Lalamu Studio。该工具整合了TTS和lip sync功能&#xff0c;可以让任意视频中的人物开口说话&#xff0c;并精确模拟口型。 例如&#xff0c;选择一段视频素材&#xff0c;添加由Ai合成的语音&#xff0c;即可完成…

为什么要选择“零代码”开发的智慧能源管理平台?

全球低代码市场发展较早&#xff0c;集中度逐渐凸显&#xff0c;零代码市场尙未形成市场格局&#xff0c;很多企业出现“业务部门不懂技术&#xff0c;技术部门不懂业务”的现象往往会制约软件的开发进度&#xff0c;如何快速搭建软件系统应用&#xff0c;助力业务增长与效率提…

京东云开发者DDD妙文欣赏(1)

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 京东云开发者原文链接&#xff1a;DDD落地实践-架构师眼中的餐厅>>&#xff0c;以下简称《餐厅》。 我截图时&#xff0c;阅读量有6044&#xff0c;在同类文章中已经算是热文了…

山西电力市场日前价格预测【2024-01-20】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-01-20&#xff09;山西电力市场全天平均日前电价为304.16元/MWh。其中&#xff0c;最高日前电价为486.22元/MWh&#xff0c;预计出现在18:15。最低日前电价为87.43元/MWh&#xff0c;预计出…

adb 配对+无线连接

配对 打开手机开发者选项-无线调试-使用配对码配对设备 出现ip端口和配对码后&#xff0c;电脑输入命令&#xff1a; adb pair ip:端口 eg:adb pair 192.168.137.244:39683 提示输入配对码&#xff1a;就按照手机上的输入。 此时配对成功 连接 再使用命令adb connect ip:port…

Java工具类:将xml转为Json

目录 一、场景二、工具类三、测试类四、测试结果 一、场景 在对接第三方接口时&#xff0c;由于接口返回的并不是常见的Json&#xff0c;而是XML&#xff0c;所以需要将XML转为Json&#xff0c;方便后续处理 二、工具类 package com.xxx.util;import org.apache.commons.lang…

力扣 | 15. 三数之和

暴力解法import java.util.*;public class _15_ThreeSum1 {public List<List<Integer>> threeSum(int[] nums) {if (nums null || nums.length < 3)return new ArrayList<>();Set<List<Integer>> res new HashSet<>();Arrays.sort(nu…

Linux的常用命令

查看命令的帮助 命令名 --help 切换目录命令cd cd app 切换到app目录 cd .. 切换到上一层目录 cd / 切换到系统根目录 cd ~ 切换到用户主目录 cd - 切换到上一个所在目录 使用tab键来补全文件路径 列出文件列表&#xff1a;ls ll ls(list)是一个非常有用的命令&…

排序:计数排序

目录 思想&#xff1a; 操作步骤&#xff1a; 思路&#xff1a; 注意事项&#xff1a; 优缺点&#xff1a; 代码解析&#xff1a; 完整代码展示&#xff1a; 思想&#xff1a; 计数排序又称为鸽巢原理&#xff0c;是对哈希直接定址法的变形应用。 操作步骤&#xff…