目录
概述
链路追踪的功能
- 快速定位链路故障
- 便于查看链路调用流程、服务依赖关系
- 可分析链路耗时,针对耗时多的部分使用多线程、异步、缓存等方式进行优化
链路id
- trace id:唯一标识一条链路行踪
- span id:唯一标识一个http请求,对应分布式系统中一个环节
一条链路对应一个trace id,往往包含多个span id。
常见的APM (Application Performance Management 应用性能分析管理) 工具
- ZipKin:twitter开源的组件,轻量、使用简单,但要侵入应用代码,功能单一,只能查看链路调用流程、各阶段的耗时情况。
- SkyWalking:国人开源的组件,已成为apache孵化的项目,不但能做链路追踪,还能监控应用状态,功能丰富强大,部署稍复杂一些。如果要结合elk搭建分布式日志系统,会对应用代码有侵入。
- PinPoint:韩国人开源的组件
- Cat:大众点评开源的组件
skywalking对应用性能的影响最小,zipkin对应用性能的影响居中,如果只是分析链路耗时,优先选择轻量级的zipkin;如果要分析链路调用的更多详细信息,优先选择skywalking。
apm工具一般都支持多种数据存储方式,比如直接存储在内存中、定时清理,持久化到es、mysql、nosql数据库中。生产环境一般是持久化链路数据到es中。
Zipkin的使用
sleuth埋点、收集链路数据,zipkin传输、展示链路数据。
搭建zipkin server
jar包下载地址:https://zipkin.io/pages/quickstart.html
#链路数据直接存储在内存中
#nohup java -jar zipkin-server-2.23.2-exec.jar > zipkin-server.log &
#持久化链路数据到es,es集群有多个url时逗号分隔
STORAGE_TYPE=elasticsearch ES_HOSTS=http://127.0.0.1:9200 nohup java -jar zipkin-server-2.23.2-exec.jar > zipkin-server.log &
访问 ip:9411 即可进入zipkin界面。
存储作为数据源,UI请求数据源获取数据展示出来。
更多细节参考:https://github.com/openzipkin/zipkin/blob/master/zipkin-server/README.md
zipkin、skywalking都是通过ip:port访问界面,如果只是单机部署,可以直接开放端口访问;如果是集群部署,用nginx做代理。
zipkin dependencies
如果链路数据持久化存储,不能直接看到链路服务之间的调用关系,需要额外引入zipkin dependencies。
jar包下载地址:https://search.maven.org/search?q=g:io.zipkin.dependencies
也可去maven仓库下载。
#执行jar包
STORAGE_TYPE=elasticsearch ES_HOSTS=http://127.0.0.1:9200 nohup java -jar zipkin-dependencies-2.6.1.jar > zipkin-dependencies.log &
更多细节可参考:https://github.com/openzipkin/zipkin-dependencies
在服务中添加zipkin的相关配置
网关、消费者、提供者都可以作为zipkin client,注册中心不需要作为zipkin client,就算注册中心作为zipkin client,也只是单独的一条链路。
创建时勾选Spring Cloud Tracing -> Zipkin Client,也可以手动添加依赖
<!-- 不需要加这个 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!--已经包含了spring-cloud-starter-sleuth,不需要再添加sleuth的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
spring:
zipkin:
#zipkin服务器的地址,默认 http://localhost:9411/
base-url: http://localhost:9411/
#需不需要?
#discovery-client-enabled: false #不用开启服务发现
sleuth:
sampler:
#采样率,0-1上的小数,0.5即50%,默认0.1
probability: 1.0
注意:采样率对性能有影响,采样率越大越影响应用性能,生产环境采样率设置低些,测试可设置为1。
日志示例
[order-service,96f95a0dd81fe3ab,852ef4cfcdecabf3,false]
第一个值,spring.application.name的值
第二个值,96f95a0dd81fe3ab ,sleuth生成的一个ID,叫Trace ID,用来标识一条请求链路,一条请求链路中包含一个Trace ID,多个Span ID
第三个值,852ef4cfcdecabf3、spanid 基本的工作单元,获取元数据,如发送一个http
第四个值:false,是否要将该信息输出到zipkin服务中来收集和展示。
SkyWalking的使用
部署skywalking web
下载地址:http://skywalking.apache.org/downloads/
SkyWalking APM -> Distribution -> 选择合适版本的tar下载
#解压、重命名
tar -xzvf apache-skywalking-apm-es7-8.2.0.tar.gz
rm -I apache-skywalking-apm-es7-8.2.0.tar.gz
mv apache-skywalking-apm-es7 skywalking
cd skywalking
vim config/application.yml
#指定集群方式,默认standalone 单机版、不集群
cluster:
selector: ${SW_CLUSTER:standalone}
#存储方式默认使用内存数据库h2,改为es7以持久化数据
storage:
selector: ${SW_STORAGE:elasticsearch7}
#其它一般不用改,改nameSpace、clusterNodes即可
elasticsearch7:
#索引前缀,索引中自动包含年月日
nameSpace: ${SW_NAMESPACE:"skywalking"}
#es集群节点
clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200}
protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}
#存储单位,默认1,以1天为单位进行存储,把一天的日志存储为一个单独的index
dayStep: ${SW_STORAGE_DAY_STEP:1}
#分片数量
indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:1}
#副本数量
indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:1}
#如果es设置了用户名、密码,需要在此处填写
user: ${SW_ES_USER:""}
password: ${SW_ES_PASSWORD:""}
如果 skywalking 8.3 修改application.yml后,保存时一直提示 文件已变化,无法保存修改的内容,可以换一个skywalking版本。
vim webapp/webapp.yml
server:
#指定skywalking web使用的端口,默认8080
port: 8080
collector:
path: /graphql
ribbon:
ReadTimeout: 10000
#指定链路数据的传送地址,有多个时逗号分隔,默认127.0.0.1:12800
listOfServers: 127.0.0.1:12800
#启动skywalking web,实际会启动2个进程
bin/startup.sh
jps看到有OAPServerStartUp、skywalking-webapp.jar2个进程才算启动成功,并非看到2个successfully就是启动成功,如果只有一个进程,一般是 config/application.yml 中的配置不对。kill -9杀死2个进程即停止。
浏览器访问 skywalking web的 ip:port 即可进入skywalking的界面。
部署服务
每部署一个服务时,就拷贝一份skywalking的agent目录。不管是部署注册中心、网关、还是其它服务,都这么做。
编辑拷贝得到的agent的config/agent.config
#指定服务名称
agent.service_name=${SW_AGENT_NAME:eureka-server}
#每3s的采样率,0表示不采样,负数表示100%采样
agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE:-1}
#指定链路数据的传送地址,默认127.0.0.1:11800
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:127.0.0.1:11800}
agent的plugins目录是已启用的插件,optional-plugins目录中是可选的插件。
plugins中默认没有springcloud getway插件,不支持监控springcloud gateway网关服务。
如果当前要部署的服务是springcloud getway网关服务,需要把optional-plugins中的apm-spring-cloud-gateway-2.1.x-plugin-8.2.0.jar拷贝到plugins中。
其它需要插件支持的中间件和框架也是同理操作。
#部署服务,要换为对应的agent下skywalking-agent.jar的路径,-javaagent要在-jar之前
nohup java -javaagent:agent/skywalking-agent.jar -jar xxx.jar > xxx.log &
#如果使用外置tomcat部署,linux版tomcat需要修改bin/catalina.sh,在首行加上
CATALINA_OPTS="$CATALINA_OPTS -javaagent:agent/skywalking-agent.jar"; export CATALINA_OPTS
#win版tomcat需要修改bin/catalina.bat,在首行加上
set "CATALINA_OPTS=-javaagent:agent/skywalking-agent.jar"
应用日志中输出trace_id
在kibana中查看链路数据时,有trace_id字段,但服务输出的日志中没有trace_id。
可以在各个服务中添加如下配置,使应用输出的日志中包含trace_id,建议加上这些配置。
pom.xml
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>8.3.0</version>
</dependency>
logback-spring.xml
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<!-- %tid即trace_id -->
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
</layout>
</encoder>
</appender>
分布式日志系统ELK
ELK(ElasticSearch Logstash Kibana),es用于存储日志,logstash将各节点的日志传输到es上,kibana对es中的日志进行查看、分析统计。
logstash是一个开源的日志收集引擎,可以实现日志的实时传输,可以从多个日志源读取日志,并进行过滤,传输到目的地。支持多种日志源,日志源可以是日志文件、数据库、统计表等,可读取系统日志、服务器日志、应用日志等。
logstash的安装部署
下载地址:https://www.elastic.co/cn/downloads/logstash
es、logstash、kibana的版本尽量保持一致。
#解压
tar -xzvf logstash-7.10.0-linux-x86_64.tar.gz
rm -I logstash-7.10.0-linux-x86_64.tar.gz
mv logstash-7.10.0 logstash
cd logstash
修改 config/jvm.options 中的堆内存设置
-Xms1g
-Xmx1g
config下新建文件 logstash.conf
input {
file {
path => "/app/eureka-server.log"
#type指定es index中type字段的值
type => "eureka-server"
# start_position => "beginning" #默认只读取实时输入,可以指定为从文件开始处读取
}
file {
path => "/app/getway.log"
type => "getway"
}
file {
path => "/app/user-server.log"
type => "user-server"
}
file {
path => "/app/order-server.log"
type => "order-server"
}
}
#filter可选
filter {
}
output {
#stdout {} #原样输出到控制台
#stdout{codec => rubydebug} #使用ruby库进行编解码
#输出到es
elasticsearch {
#值是字符串数组
hosts => ["http://localhost:9200"]
#index 默认值是"logstash-%{+YYYY.MM.dd}",可以自行指定,index美岑不允许包含大写字母
#index => "mall-log-%{+YYYY.MM.dd}"
#user => "xxx"
#password => "xxx"
}
}
- input:指定输入,可包含多个输入。如果日志流量大,可以将日志传输到kafka,从kafka读取日志输入。
- filter:对日志过滤处理,常见的插件比如 grok 匹配得到指定字段,常用来获取message中的日志打印时间、trace_id。
- output:指定输出,一般为es、file。
三者都可以包含多个组件,logstash的config下有官方提供的配置模板logstash-sample.conf,可以照着写。
input、filter、output的插件使用可参考:https://www.elastic.co/guide/en/logstash/current/index.html
#启动logstash,-f指定配置文件位置。logstash.bat是win版用的
bin/logstash -f config/logstash.conf &
logstash也是java编写的,可以jps查看,kill -9终止。
logstash自带了jdk,在根目录下有个jdk的目录,但版本往往很新,一般不用。
logstash很吃资源,可以使用轻量级的 filebeat 代替。
kibana查看链路数据、日志
1、Management -> Stack Management -> Kibana -> Advanced Settings 设置预选项。这些预选项很有用,可以指定Date格式、时间段范围等。
2、在Index Patterns中指定的index匹配规则
3、Kibana -> Discover -> 选择上一步建立的index匹配规则 -> 查看、筛选document
链路数据可以根据 trace_id 字段进行筛选;日志可以根据message字段中的trace_id进行筛选,根据timestamp排序。trace_id可以在skywalking的web页面中复制。
es日志文档中自带了@timestamp字段,但这个只是logstash接受到日志的时间,并非应用打印日志的时间,根据@timestamp排序看到的日志记录顺序往往有一些出入。最好是在logstash的filter中用grok插件从message中获取时间,查看日志时先用trace_id筛选,再根据时间排序。