分布式链路追踪系统Skywalking的部署和应用

一,背景

随着业务的扩张,系统变得越来越复杂,由前端、app、api、微服务、数据库、缓存、消息队列、关系数据库、列式数据库等构成了繁杂的分布式网络。 当出现一个调用失败的问题时,要定位异常在哪个服务,需要进入每一个服务里看日志,这个过程的复杂度和工作量是不可想象的。

​当讲到大型微服务系统时, 下面这张图经常被引用到。

为了解决故障定位难,链路梳理难,容量预估难的问题,一般引入APM体系统来解决,而链路追踪则是APM中尤为重要的一环。有了链路追踪, 我们可以做到:

  1. 请求链路追踪,故障快速定位:可以通过调用链路并结合业务日志快速定位问题所在
  2. 可视化:展示各阶段耗时, 进行性能瓶颈分析
  3. 应用拓扑:梳理服务依赖关系并加以优化
  4. 数据分析:汇总分析用户的行为路径

二,术语

APM: 应用系统的实时监控,用于实现性能管理和故障管理

Dapper: google一篇论文里提到, 主要详谈分布式跟踪服务的设计

prometheus: 服务监控系统

grafana:度量分析和可视化工具

zipkin:分布式的跟踪系统

cat: 大众点评开发的实时应用监控平台

skywalking:Apache顶级项目的链路跟踪系统

ELK:Elasticsearch、Logstash和Kibana三大开源框架

EFK:elasticsearch、filebeat和kibana

Filebeat:golang实现的日志采集器

三,APM主要解决的问题

  1. Metrics集中式度量系统 (prometheus+grafana),用于可聚合的数据
  2. Tracing分布式全链接追踪系统 (zipkin,cat,skywalking等),用于请求范围内的信息
  3. Loging集中日志系统 (ELK, EFK, Filebeat+ELK),用于记录离散的事件

三者有相互重叠的部分

四,技术选型

阿里的鹰眼, 点评的cat :闭源或侵入式
zipkin :可视化方面做得太简单

这个三个框架从技术选型上排除掉。下面主要从pinpoint和skywalking这两个作对对比

|-------------|-------------|------------------|
| 对比项 | Pinpoint | Skywalking |
| opentracing | 否 | 是 |
| 协议 | thrift | gRPC |
| 存储 | hbase+mysql | es,mysql,h2,tidb |
| ui丰富度 | 高 | 一般 |
| 代码侵入式 | 无 | 低 |
| 性能损耗 | 高 | 低 |
| 部署难度 | 高 | 低 |

通过对比可以看到,Pinpoint和Skywalking不相上下,各有优劣,从界面、操作,集成方式来说,Pinpoint更好, 不过因为种种不得已的原因,我们今天还是聚焦在Skywalking上,它的优点是部署难度低,监控范围广、维度多,对代码侵入少,系统性能损失低,还支持接入 ELK 进行存储展示。

其他限制

1.只支持已知的代理,如果使用的中间件还未被支持,需要自己写插件。

2.跨线程的场景不支持自动代理,比如任务分配,任务池,批处理的场景。

五,skywalking原理

什么是span

下图描述的是树结构的Span集合,表示一次完整的跟踪,从请求到服务器开始,服务器返回response结束,跟踪每次rpc调用的耗时,存在唯一标识trace_id。

什么是skywalking

  • 客户端是通过Agent,与Collector相连接,然后Collector将数据存储在Es中。
  • 监控页面是连接的Collector,Collector从Es中将数据查询出来。
  • 直接和数据打交道的是Collector。

六,部署

1, 部署elk

docker run -dit --name elk \

-p 5601:5601 \

-p 9200:9200 \

-p 5044:5044 \

-v /data/elk-data:/var/lib/elasticsearch \

-v /etc/localtime:/etc/localtime \

sebp/elk:700

2,安装Skywalking server

docker run --name oap --restart always -d \

-e TZ=Asia/Shanghai \

-p 12800:12800 \

-p 11800:11800 \

--link elk:es7 \

-e SW_STORAGE=elasticsearch7 \

-e SW_STORAGE_ES_CLUSTER_NODES=es7:9200 \

apache/skywalking-oap-server:8.2.0-es7
docker run -d --restart always --name skywalking-ui \

-e TZ=Asia/Shanghai \

-p 18080:8080 \

--link oap:oap \

-e SW_OAP_ADDRESS=oap:12800 \

apache/skywalking-ui:8.2.0

访问地址:http://服务器IP/18080

七,无侵入跟踪采集

注意:Skywalking并不是无侵入的,只是可以用无侵入这种来用,实际上要用traceId查询的话,还是要侵入代码,这是它不安全的地方!!!

1,如果是准备用无侵入的方式接入采集的话,agent-jar包所在的下载地址

1, 下载:wget https://dlcdn.apache.org/skywalking/java-agent/9.0.0/apache-skywalking-java-agent-9.0.0.tgz

2, 解压缩: tar -zxvf apache-skywalking-java-agent-9.0.0.tgz

3, 在解压后的文件夹中有 skywalking-agent.jar

2,运行jar时,加入jvm选项

-javaagent:\path\skywalking-agent.jar -Dskywalking.agent.service_name={service_name} -Dskywalking.collector.backend_service={ip}:{port}
注意:上面一行要放在 -jar选项之前

例如:

java -javaagent:/root/apm/skywalking-agent.jar -Dskywalking.agent.service_name=myName -Dskywalking.collector.backend_service=127.0.0.1:11800 -jar xxxx.jar

八,侵入式记录traceid到日志

1,引入pom

复制代码
 <dependency>
     <groupId>org.apache.skywalking</groupId>
     <artifactId>apm-toolkit-trace</artifactId>
     <version>6.5.0</version>
</dependency>

2, 修改log4j.xml的pattern

日志展现结果, 有了traceid,parrent spanid, spanid, 使得有ELK统一日志系统把具体业务

复制代码
<PatternLayout 
  pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] %-5level [%X{TRACE_ID},%X{SPAN_ID}] - %msg%xEx%n"/>

3, filter的实现

复制代码
@Component
public class TraceIdFilter extends OncePerRequestFilter {
    private static final String TRACE_ID = "TRACE_ID";
    private static final String SPAN_ID = "SPAN_ID";
    private static final String SPAN_PID = "SPAN_PID";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String traceId = TraceContext.traceId();
        if(null == traceId){
            chain.doFilter(request, response);
            return;
        }

        String spanPid = request.getHeader(SPAN_PID);
        // 生成spanId
        String spanId;
        if(spanPid == null){
            spanPid = "0";
            spanId = "1";
        }
        else {
            spanId = String.valueOf(Integer.valueOf(spanPid) +1);
        }
        SpanContext.getContext().initContext(spanId);
        MDC.put(TRACE_ID, traceId);
        MDC.put(SPAN_ID, spanId);
        MDC.put(SPAN_PID, spanPid);
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        MDC.clear();
    }
}

4, feign拦截器的实现

复制代码
public class FeignClientInterceptor implements RequestInterceptor {
    private static final String SPAN_PID = "SPAN_PID";
    @Override
    public void apply(RequestTemplate requestTemplate) {
        try {
            SpanContext spanContext = SpanContext.getContext();
            if (Objects.nonNull(spanContext)) {
                requestTemplate.header(SPAN_PID, spanContext.getSpanId());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
SpanContext
@Data
public class SpanContext {
    private String spanId;
    private static ThreadLocal<SpanContext> LOCAL = new ThreadLocal<>();

    public static SpanContext getContext() {
        SpanContext context = LOCAL.get();
        if (Objects.isNull(context)) {
            context = new SpanContext();
            LOCAL.set(context);
        }
        return context;
    }

    /**
     * 初始化
     */
    public void initContext(String spanId){
        this.spanId = spanId;
    }
}

九,UI界面

简单记录一下,实际上我是不喜欢这个工具,没有PP好用!

码字不易,记得点赞关注哟!

相关推荐
躺不平的理查德4 小时前
General Spark Operations(Spark 基础操作)
大数据·分布式·spark
talle20214 小时前
Zeppelin在spark环境导出dataframe
大数据·分布式·spark
渣渣盟5 小时前
大数据开发环境的安装,配置(Hadoop)
大数据·hadoop·分布式
Angindem5 小时前
SpringClound 微服务分布式Nacos学习笔记
分布式·学习·微服务
龙仔72514 小时前
离线安装rabbitmq全流程
分布式·rabbitmq·ruby
〆、风神16 小时前
Spring Boot 整合 Lock4j + Redisson 实现分布式锁实战
spring boot·分布式·后端
胡萝卜糊了Ohh17 小时前
kafka
分布式·kafka
桑榆080619 小时前
Spark-Streaming核心编程
大数据·分布式·spark
nbsaas-boot21 小时前
分布式微服务架构,数据库连接池设计策略
分布式·微服务·架构