可. 概述
随着微服务的普及,应用多节点部署和请求跨服务调用越来越常见,开发人员定位问题的成本越来越高。例如当一个订单出现问题的时候,传统的处理方式是挨个看每个服务节点的日志,找到发生问题的节点再针对性的分析,但是这种方式既耗时,又不直观,效率很慢,于是统一日志中心应运而生。但是有了日志中心后,怎样在一次查询结果中,把某个请求流经的所有服务节点日志按照时间先后顺序一次性展示,又引出了链路追踪的需求。
. 链路追踪工具构成
一般的链路追踪工具主要包含三种组件,客户端,收集器,性能分析服务端,各种框架略有差异的,但是基本都是一样的套路:
1. 客户端是集成在应用内部或是随应用一起启动的代理进程,主要负责应用数据收集和上报;
2. 收集器是负责统一收集各个客户端上报的数据,持久化,供服务端进行分析;
3. 性能分析服务端负责对数据进行拼装,分析,UI展示
. 链路追踪工具原理
链路追踪工具采集的数据,除了运用性能分析的指标,例如接口时间,请求参数等之外,链路本身的数据一般是通过三个ID来串联:traceId,spanId,parentId。
traceId在整个请求链路中都不会变,我们进行全链路日志查询的时候,也是用的traceId;
spanId在某个代码块中不会变,这个代码块的范围可以自定义,也可以由框架自主划定,框架一般默认的spanId共享范围是一个方法内部,比如A方法调用B方法,则A方法日志打印出来的spanId都是xxx,B方法日志打印出来的spanId都是yyy。parentId是连接两个相邻的span的id,比如A方法调用B方法,B方法调用C方法,A,B,C的spanId分别是xxx, yyy, zzz,则B方法内的parentId是xxx,C方法内的parentId是yyy.
性能分析服务端就是通过spanId和parentId将一条条小线段串联为一整条链路,所以服务端的UI界面上一般都能展示出服务topo图。
开源的链路追踪工具很多,比如zipkin,SkyWalking,pinpoint,jaeger等。以下就以jaeger为例,介绍一下springcloud服务集成和搭建的过程以及效果。
. jaeger安装
jaeger分为jaeger-agent, jaeger-collector, jaeger-query,分别为客户端,收集器,性能分析服务端。可以按照官网文档依次安装,也可以直接运行all-in-one这个容器(https://www.jaegertracing.io/docs/1.45/getting-started/)。
. springcloud集成jaeger客户端方式
1. pom文件添加依赖
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
<version>3.3.1</version>
</dependency>
2. 配置文件添加jaeger服务端配置
opentracing:
jaeger:
http-sender:
url: http://localhost:14268/api/traces
3. logback配置文件修改日志格式:
%d{yyyy-MM-dd HH:mm:ss.SSS}#-#%X{X-B3-TraceId:-}#-#%-5level#-#%logger{35}#-#%msg%n
4. 线程池场景下,保证traceid能正常传递,需进行以下处理:
b. 添加这两个类到工程:
http://182.43.12.244:9090/offerFile/jaeger/MDCScopeManager.java
http://182.43.12.244:9090/offerFile/jaeger/TracingConfig.java
c. 线程池的初始化方式改为类似这样:
import io.opentracing.Tracer;
import io.opentracing.contrib.concurrent.TracedExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class JaegerThreadExcutor {
@Autowired
private Tracer tracer;
@Bean
public Executor executor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(5);
threadPoolTaskExecutor.setMaxPoolSize(10);
threadPoolTaskExecutor.initialize();
return new TracedExecutor(threadPoolTaskExecutor, tracer);
}
}
5. 消息队列场景
opentracing-spring-jaeger-cloud-starter已经集成了对kafka和rabbitmq等消息中间件的支持,无需额外处理
. jaeger性能分析服务端效果图
点击后可以查看span详情:
. 其他语言的客户端及中间件支持
某些语言原生客户端支持不完善的,可以使用opentelemetry提供的相关集成方案。
opentelemetry是链路追踪的统一规范,提供了一套接口规范和配套工具。同样也是适配于jaeger工具。
opentelemetry官网提供了主流语言和框架的集成方案,可以参考:
https://opentelemetry.io/ecosystem/registry/?component=instrumentation
. 统一日志中心
链路追踪工具自带的客户端可以直观的看到服务topo图,追踪链路细节,甚至可以看到每个请求的报文,或者数据库查询sql,对于分析系统性能瓶颈比较有帮助。但是统一日志中心是对业务日志的统一管理和检索,可以协助开发人员根据业务标识快速定位问题。二者相辅相成。
. 统一日志中心组件技术选型
最流行的日志中心应该是ELK了,但是ELK的方案也有很多不如意的地方。比如logstash占用资源过多,elasticsearch也占用资源多,集群维护成本较高。logstash的替代方案比较多,对于轻量化的使用场景,可以使用filebeat代替,filebeat占用资源远少于logstash;对于业务深度定制化的场景,可以使用flume集成自定义的二开sink或者source组件。elasticsearch替代方案不多,但是近年来出了loki可以用于替代es,loki比es更轻量,占用资源远小于es,但是只能用配套的promtail作为日志采集客户端。
. promtail配置
对于以上的logback日志格式(%d{yyyy-MM-dd HH:mm:ss.SSS}#-#%X{X-B3-TraceId:-}#-#%-5level#-#%logger{35}#-#%msg%n),可参考使用以下promtail配置来完成日志采集:
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://10.63.143.41:3100/loki/api/v1/push
- job_name: upms_business_logs
static_configs:
- targets:
- localhost
labels:
service_name: upms
logType: businessLog
__path__: /mnt/logs/upms/cloud-upms.log
pipeline_stages:
- regex:
expression: "^(?s)(?P<logTime>.*)#-#(?P<traceId>\\S*)#-#(?P<level>.*)#-#(?P<className>\\S+)#-#(?P<msg>.*)$"
- labels:
level: level
经过以上配置处理后,日志中的日志级别和服务名会作为label存储在loki中,grafana(和kibana类似)中可根据多维度进行日志检索
以上查询条件,可以查询出日志级别为INFO,服务名为upms,traceid=61da07451d521138的所有日志