分布式链路追踪系统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好用!

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

相关推荐
KIDAKN4 小时前
RabbitMQ 初步认识
分布式·rabbitmq
pan3035074794 小时前
Kafka 和 RabbitMQ的选择
分布式·kafka·rabbitmq
hzulwy7 小时前
Kafka基础理论
分布式·kafka
明达智控技术8 小时前
MR30分布式IO在全自动中药煎药机中的应用
分布式·物联网·自动化
jakeswang8 小时前
细说分布式ID
分布式
失散1310 小时前
分布式专题——1.2 Redis7核心数据结构
java·数据结构·redis·分布式·架构
王中阳Go10 小时前
头一次见问这么多kafka的问题
分布式·kafka
boonya12 小时前
Kafka核心原理与常见面试问题解析
分布式·面试·kafka
KIDAKN13 小时前
RabbitMQ 重试机制 和 TTL
分布式·rabbitmq
JAVA学习通13 小时前
【RabbitMQ】----初识 RabbitMQ
分布式·rabbitmq