分布式链路追踪Skywalking

简介

  skywalkings是2015年开源的一款国产框架,2017年的时候加入了Apache孵化器。skywalking是分布式应用程序的性能监控工具,具有多种监控手段,作为APM工具,它具有分布式追踪、性能指标分析、应用和服务依赖分析等功能。可以通过语言探针来获取监控数据。专门是为了微服务(spring cloud)、云原生架构与容器架构(docker/k8s)而设计的。

  Skywalking整体架构一共分四块:

    Skywalking-Agent(项目引入,Skywalking-Agent 收集应用程序链路信息,交给Skywalking-OAP处理器)
    Skywalking-OAP-Server 把 Skywalking-Agent 发过来的 Tracing 数据交给 Analysis Core 分析,最后将结果存储到外部存储器。
    数据存储(H2/mysql/ElasticSearch)
    Skywalking UI 负责给用户查看链路等信息

  

安装

1. 安装存储器es

1】创建好配置和数据目录,并配置所有端口都可以访问
[root@node1 wulei]# mkdir -p /usr/local/wulei/elasticsearch/config
[root@node1 wulei]# mkdir -p /usr/local/wulei/elasticsearch/data
[root@node1 wulei]# echo "http.host: 0.0.0.0" > /usr/local/wulei/elasticsearch/config/elasticsearch.yml
[root@node1 wulei]# chmod -R 777 /usr/local/wulei/elasticsearch/

【2】下载并启动 9200是我们rest请求端口,9300是集群交互端口
docker run --name wl-es -p 9200:9200 -p 9300:9300 --privileged=true -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms512m -Xms512m" -v /usr/local/wulei/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /usr/local/wulei/elasticsearch/data:/usr/share/elasticsearch/data -v /usr/local/wulei/elasticsearch/plugins:/usr/share/elasticsearch/plugins -d elasticsearch:7.16.1

查看es首页 http://192.168.200.100:9200/

2. 安装oap

docker run --name wl-oap --restart always -d \
-e TZ=Asia/Shanghai -p 12800:12800 -p 11800:11800 \
--link wl-es -e SW_STORAGE=elasticsearch7 \
-e SW_STORAGE_ES_CLUSTER_NODES=wl-es:9200 \
apache/skywalking-oap-server:8.5.0-es7

参数:
--link <name or id>:alias ,添加到另一个容器的链接,可以添加别名或者不加
–link后面的参数和elasticsearch容器名⼀致;
SW_STORAGE=elasticsearch7 是固定的,使用es7;
SW_STORAGE_ES_CLUSTER_NODES:wl_es7也可改为es服务器部署的Ip地址,比如ip:9200

3. 安装ui界面

docker run -d --name wl-skywalking-ui \
--restart=always \
-e TZ=Asia/Shanghai \
-p 8080:8080 \
--link wl-oap \
-e SW_OAP_ADDRESS=wl-oap:12800 \
apache/skywalking-ui:8.5.0

  SkyWalking UI界面访问地址 ip:8080
  这时候看es索引,会发现有很多 SkyWalking 的索引了
  http://192.168.200.100:9200/_cat/indices?v=true&pretty。    sw开头的都是。

仪表盘

1. Skywalking ui控制栏
    仪表盘:查看被监控服务的运行状态
    拓扑图:以拓扑图的方式展现服务的关系
    追踪:以接口的列表方式展现
    性能剖析:对端点进行采样分析
    日志:可查看服务日志
    告警:触发告警的告警列表,包括了服务的失败率,超时等待

2. 展示栏(Global全局维度)
    Global、Service、Instance、Endpoint不同展示面板
    Services load:服务每分钟请求数
    Slow Services:慢响应服务,单位ms
    Un-Health services(Apdex): Apdex性能指标,1为满分
    Slow Endpoint:慢响应端点,单位ms
    Global Response Latency:百分比响应延时,不同百分比的延时时间,单位ms
    Global Heatmap:服务响应时间热力分布图,根据时间段内不同响应时间的数量显示颜色深度;
    底部栏:展示数据的时间区间,点击可以调整

3. 展示栏(Service服务维度)
    Service Apdex(数字):当前服务的评分
    Service Apdex(折线图):不同时间的Apdex评分
    Service Avg Response Times:平均响应延时,单位ms
    Service Response Time Percentile:百分比响应延时
    Successful Rate(数字):请求成功率
    Successful Rate(折线图):不同时间的请求成功率
    Servce Load(数字):每分钟请求数
    Servce Load(折线图):不同时间的每分钟请求数
    Servce Instances Load:每个服务实例的每分钟请求数

4. 展示栏(Instance服务维度,不过对于监控CPU、内存等,Promethus 是个更好的选择)
    Service instance load:当前实例的每分钟请求数
    Service Instance Successful Rate:当前实例的请求成功率
    Service Instance Latency:当前实例的响应延时
    JVM CPU:jvm占用CPU的百分比
    JVM Memory:JVM内存占用大小,单位m
    JVM GC Time:JVM垃圾回收时间,包含YGC和OGC
    JVM GC Count:JVM垃圾回收次数,包含YGC和OGC
    JVM Thread Count:JVM线程数

5. 展示栏(Endpoint维度,即接口维度的意思)
    Endpoint Load in Current Service:每个端点的每分钟请求数
    Slow Endpoints in Current Service:每个端点的最慢请求时间,单位ms
    Successful Rate in Current Service:每个端点的请求成功率
    Endpoint Load:当前端点每个时间段的请求数据
    Endpoint Avg Response Time:当前端点每个时间段的请求行响应时间
    Endpoint Response Time Percentile:当前端点每个时间段的响应时间占比
    Endpoint Successful Rate:当前端点每个时间段的请求成功率

6. 接口追踪
  左侧:接口列表,请求为红色表示异常,蓝色表示正常
  右侧:追踪列表,api的各个连接点按照端点的先后顺序和时间排序

 

Agent

1. 下载 https://skywalking.apache.org/downloads/。(如下图,解压后整个文件夹都需要用到,不能只复制 skywalking-agent.jar ,它会依赖到其他包)

2. 指定运行参数

-javaagent:C:\Users\Administrator\Desktop\apache-skywalking-apm-bin\agent\skywalking-agent.jar
-Dskywalking.agent.service_name=Shop_Service
-Dskywalking.collector.backend_service=192.168.200.100:11800

3. 此时启动项目后先访问接口,再刷新下 skywalking 就能看到我们的面板信息了。

查看效果

  我们通过探针的形式收集数据,能看到服务的整体监控信息,响应统计、接口耗时、gc信息、执行的sql语句。但它只能看到服务与服务之间的链路信息(上游节点1 —> 本节点 —> 下游节点1。想要整个链路都能看到,那就整个链路都用探针监听就好了),看不到服务内部具体的信息(服务内部方法a调用方法b),看内部信息我们需要接入链路追踪的方式。

链路追踪

<!-- skywalking链路追踪 -->
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-trace</artifactId>
    <version>8.5.0</version>
</dependency>
        
        
/**
 * @Tag(key = "方法名-input", value = "arg[0]"),arg[0] 表示第一个参数
 * @Tag(key = "方法名-output", value = "returnedObj"),returnedObj 是固定写法
 */
@Trace
@Tags(
        {
            @Tag(key = "test1-input", value = "arg[0]"),
            @Tag(key = "test1-output", value = "returnedObj")
        }
)
@Override
public List<Area> test1(Long parentId) {
    List<Area> areas = listByPId(parentId);
    test2();
    return areas;
}
方法加上 @Trace 注解后,就会把该方法(仅仅是该方法)加入链路监控了,再叠加一个 @Tags 后则能看到该方法的出参入参。如果某方法仅仅只加@Tags、由于不在链路上,所以是看不到参数的。

性能分析

新建任务:同一个服务在监听的时间段内,只能监听一个接口,该服务无法创建多个监听任务。

 进行分析

日志上报

1. 指定 skywalking oap 地址。修改  apache-skywalking-apm-bin\agent\config\agent.config 文件,指定  日志数据的grpc 的ip、prot、日志文件最大大小、上传日志超时时间。

plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:192.168.200.100}
plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
plugin.toolkit.log.grpc.reporter.upstream_timeout=${SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30}

2. 添加日志依赖

        <!--链路追踪,上报日志不需要用到这个jar-->
<!--        <dependency>-->
<!--            <groupId>org.apache.skywalking</groupId>-->
<!--            <artifactId>apm-toolkit-trace</artifactId>-->
<!--            <version>8.5.0</version>-->
<!--        </dependency>-->
        <!--日志上报 skywalking8.4.0之后才支持-->
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-logback-1.x</artifactId>
            <version>8.5.0</version>
        </dependency>

3. 指定日志输出格式

    <!--控制台日志-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <charset>UTF-8</charset>
            <pattern>%date [%thread] [%file:%line] [%level %logger{0}] - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 随意写一个 appender ,甚至可以不在下面的 <root>中引用他 。
         不写一个 TraceIdPatternLogbackLayout 的日志,启动时会报错找不到[tid],tid是在 TraceIdPatternLogbackLayout 类中定义好的。
         如果我们在上面的console中使用[tid]也会报错说找不到,所以通常我们直接用该appender做console就好了
         -->
    <appender name="wulei-test" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <charset>UTF-8</charset>
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</pattern>
            </layout>
        </encoder>
    </appender>
    <!-- 上报到skywalking -->
    <appender name="skywalking" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>

    <root level="${LOG_LEVEL}">
        <appender-ref ref="console" />
        <appender-ref ref="skywalking" />
    </root>
解释说明

最终的日志配置

    <!--控制台日志-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <charset>UTF-8</charset>
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</pattern>
            </layout>
        </encoder>
    </appender>
    <!-- 上报到skywalking -->
    <appender name="skywalking" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>

    <root level="${LOG_LEVEL}">
        <appender-ref ref="console" />
        <appender-ref ref="skywalking" />
    </root>

告警规则

  1. 在  apache-skywalking-apm-bin\config\alarm-settings.yml  中内置了一些默认告警规则。

# SkyWalking 的发行版都会默认提供config/alarm-settings.yml文件,里面预先定义了一些常用的告警规则。如下: 
# 1 最近3分钟内服务平均响应时间超过1秒
# 2 最近2分钟内服务成功率低于80%
# 3 最近3分钟的服务响应时间百分比超过1秒
# 4 最近2分钟内服务实例的平均响应时间超过1秒
# 5 最近2分钟内数据库访问的平均响应时间超过1秒
# 6 最近2分钟内端点平均响应时间超过1秒
# 7 过去2分钟内端点关系的平均响应时间超过1秒
# 
# 然后我们配置好webhook地址就好了


# Sample alarm rules.
rules:
  # 最近3分钟内服务平均响应时间超过1秒.
  service_resp_time_rule:
    metrics-name: service_resp_time
    op: ">"
    threshold: 1000
    period: 10
    count: 3
    silence-period: 5
    message: Response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes.
  # 最近2分钟内服务成功率低于80%
  service_sla_rule:
    # Metrics value need to be long, double or int
    metrics-name: service_sla
    op: "<"
    threshold: 8000
    # The length of time to evaluate the metrics
    period: 10
    # How many times after the metrics match the condition, will trigger alarm
    count: 2
    # How many times of checks, the alarm keeps silence after alarm triggered, default as same as period.
    silence-period: 3
    message: Successful rate of service {name} is lower than 80% in 2 minutes of last 10 minutes
  # 最近3分钟的服务响应时间百分比超过1秒  
  service_resp_time_percentile_rule:
    metrics-name: service_percentile
    op: ">"
    threshold: 1000,1000,1000,1000,1000
    period: 10
    count: 3
    silence-period: 5
    message: Percentile response time of service {name} alarm in 3 minutes of last 10 minutes, due to more than one condition of p50 > 1000, p75 > 1000, p90 > 1000, p95 > 1000, p99 > 1000
  # 最近2分钟内服务实例的平均响应时间超过1秒
  service_instance_resp_time_rule:
    metrics-name: service_instance_resp_time
    op: ">"
    threshold: 1000
    period: 10
    count: 2
    silence-period: 5
    message: Response time of service instance {name} is more than 1000ms in 2 minutes of last 10 minutes
  # 最近2分钟内数据库访问的平均响应时间超过1秒
  database_access_resp_time_rule:
    metrics-name: database_access_resp_time
    threshold: 1000
    op: ">"
    period: 10
    count: 2
    message: Response time of database access {name} is more than 1000ms in 2 minutes of last 10 minutes
  # 最近2分钟内端点平均响应时间超过1秒
  endpoint_relation_resp_time_rule:
    metrics-name: endpoint_relation_resp_time
    threshold: 1000
    op: ">"
    period: 10
    count: 2
    message: Response time of endpoint relation {name} is more than 1000ms in 2 minutes of last 10 minutes
#  过去2分钟内端点关系的平均响应时间超过1秒
#  默认没有打开,并提示:由于端点的数量远远多于服务和实例,端点相关度量告警将比服务和实例告警消耗更多内存
#
#  endpoint_avg_rule:
#    metrics-name: endpoint_avg
#    op: ">"
#    threshold: 1000
#    period: 10
#    count: 2
#    silence-period: 5
#    message: Response time of endpoint {name} is more than 1000ms in 2 minutes of last 10 minutes

webhooks:
  - http://192.168.200.100:8882/webhook/
#  - http://127.0.0.1/go-wechat/
配置规则与webhook

  配置讲解:

Rule name:规则名称,也是在告警信息中显示的唯一名称。必须以_rule结尾,前缀可自定义
Metrics name:度量名称,取值为oal脚本中的度量名,目前只支持long、double和int类型。
OP: 操作符,目前支持 >、<、= Period:多久告警规则需要被核实一下。这是一个时间窗口,与后端部署环境时间相匹配
Threshold:阈值
period:评估度量标准的时间长度,也就是告警检查周期,分钟为单位
Count:在一个 Period 窗口中,如果values超过Threshold值(按op),达到Count值,需要发送警报
Silence period:在时间N中触发报警后,在TN -> TN + period这个阶段不告警。 默认情况下,它和Period一样,这意味着 相同的告警(在同一个Metrics name拥有相同的Id)在同一个Period内只会触发一次
message:告警消息

  2. 编写webhook

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@Slf4j
@RestController
public class CallbackController {

    /**
     * 接收skywalking服务的告警通知并发送至邮箱
     * 必须是post
     */
    @PostMapping("/webhook")
    public void  webhook(@RequestBody List<AlarmMessage> alarmMessageList){
        String content = getContent(alarmMessageList);
        log.info("告警邮件已发送..."+content);
    }

    private String getContent(List<AlarmMessage> alarmList) {
        StringBuilder sb = new StringBuilder();
        for (AlarmMessage dto : alarmList) {
            sb.append("scopeId: ").append(dto.getScopeId())
                    .append("\nscope: ").append(dto.getScope())
                    .append("\n目标 Scope 的实体名称: ").append(dto.getName())
                    .append("\nScope 实体的 ID: ").append(dto.getId0())
                    .append("\nid1: ").append(dto.getId1())
                    .append("\n告警规则名称: ").append(dto.getRuleName())
                    .append("\n告警消息内容: ").append(dto.getAlarmMessage())
                    .append("\n告警时间: ").append(dto.getStartTime())
                    .append("\n标签: ").append(dto.getTags())
                    .append("\n\n---------------\n\n");
        }
        return sb.toString();
    }

    @Data
    public static class AlarmMessage  {
        private int scopeId;
        private String scope;
        private String name;
        private String id0;
        private String id1;
        private String ruleName;
        private String alarmMessage;
        private List<Tag> tags;
        private long startTime;
        private transient int period;
        private transient boolean onlyAsCondition;

        @Data
        public static class Tag{
            private String key;
            private String value;
        }
    }
}

  最终效果:告警面板会有告警记录,并且会把告警消息推送到webhook中。(我这边没有推送成功,如果你知道是什么原因,麻烦在评论区说一下,谢谢啦。我这边不是端口问题,webhook服务用postman是可以正常请求的)

 

posted @ 2023-06-21 16:27  吴磊的  阅读(458)  评论(0编辑  收藏  举报
//生成目录索引列表