skywalking agent 核心概念及发送逻辑

插件开发的基本概念

核心对象概念: Span, Trace Segment, ContextCarrier, ContextSnapshot

span

span 概念上可以理解为一次方法调用, rpc调用, db访问, 和 Google Dapper 的 OpenTracing 类似

分布式追踪系统 -- Opentracing

Skywalking 中 Span 的分类依据是否"跨进程"

  1. 不跨线程: LocalSpan 可以分为
    • 一次普通方法调用
    • 跨线程后记录异步线程执行信息
  2. 跨进程: RemoteSpan, 可以细分为
    • EntrySpan: "接收端 或 入口端", 例如SOA服务端 或 服务端入口端点, 例如: Controller 入口, RPC 服务端, 消息队列的消费者,
    • ExitSpan: "发送端 或 出口端", 代表应用服务的客户端, SOA 客户端, 消息队列生产者, 数据库查询请求, RPC 组件请求

Trace Segment

Trace Segment 是在一个线程中归属同一个操作的所有 span 的聚合, 这些span都有唯一的 segmentId

核心属性如下

  1. TraceSegmentId: 雪花算法生成的id
  2. Refs: 父级引用, 对于 RPC 调用只有一个, 但对于消息队列或批处理框架则可能会有多个(多个生产者)
  3. Spans: 此 segments 的集合
  4. relatedGlobalTraceId: 此 Trace Segment 的 traceId, 8.x 后只有一个了, 之前是 DistributedTraceIds
  5. ignore: 为 true 则不会上传 skywalking 的collector
  6. isSizeLimited: 从属于此 Trace Segment 数量限制, 默认300, 超过时会变成 NoopSpan

跨进程支持 ContextCarrier

skywalking 需要解决一个重要问题的是"跨进程调用链" 的收集

例如有 A -> B 的一个 RPC 调用, ContextCarrier 的处理过程如下
==客户端==

  1. A 创建一个空的 ContextCarrier
  2. 通过 ContextManager#createExitSpan 创建一个 ExitSpan
  3. 将 ContextCarrier.items 所有元素放到调用过程的请求信息中, 如 HTTP HEADER, DUBBO attachments, kafka 消息的 header 中

==服务端==

  1. B 收到请求后 ContextCarrier 传输到 服务端后, 提取所有相关信息
  2. 通过 ContextManager#createEntrySpan 绑定服务端B 和客户端A

跨线程支持 ContextSnapshot

跨进程, 跨线程处理非常类似, 都需要传播上下文, 线程之间则不需要序列化数据

过程如下

  1. 使用 ContextManager#capture 获取 ContextSnapshot 对象
  2. 子线程通过 方法参数或由现有参数访问 ContextSnapshot
  3. 在子线程中使用 ContextManager#continue

ContextManager 处理机制

处理过程分为, 收集, 发送

收集过程

核心组件为: ThreadLocal<TracingContext> CONTEXT, 用于收集 Span, 发送span 到skywalking 服务端

ContextManager#createEntrySpan 中通过 TracingContext#createEntrySpan 方法添加 Span, 其中 TracingContext 中维护了一个 LinkedList<AbstractSpan> 的栈

TracingContext#createEntrySpan 逻辑

@Override
public AbstractSpan createEntrySpan(final String operationName) {
    // 判断是否超出限制
    if (isLimitMechanismWorking()) {
        NoopSpan span = new NoopSpan();
        return push(span);
    }
    ...
    // 如果存在则提取父级span, 并开始 span 计时
    final AbstractSpan parentSpan = peek();
    if (parentSpan != null && parentSpan.isEntry()) {
        ...
        entrySpan = parentSpan;
        return entrySpan.start();
    } else {
        entrySpan = new EntrySpan(
            spanIdGenerator++, parentSpanId,
            operationName, owner
        );
        entrySpan.start();
        // 添加到栈中  activeSpanStack.addLast(span);
        return push(entrySpan);
    }
}    

发送 Span 逻辑

触发点: ContextManager#stopSpan(), 最终调用到 TracingContext#finish 方法, 主要逻辑如下

private void finish() {
    if (isRunningInAsyncMode) {
        asyncFinishLock.lock();
    }
    try {
        // Span 调用 stop 时,会对 activeSpanStack进行 pop 操作 移除元素, 因此可以用于判断是否完成
        boolean isFinishedInMainThread = activeSpanStack.isEmpty() && running;
        if (isFinishedInMainThread) {
            /*
             * Notify after tracing finished in the main thread.
             */
            TracingThreadListenerManager.notifyFinish(this);
        }
        
        // 异步处理部分
        ...
    } finally {
        if (isRunningInAsyncMode) {
            asyncFinishLock.unlock();
        }
    }
}

这块可以看到, 最终使用的的是 TracingContext.ListenerManager 进行处理, 它维护多个 TracingContextListener, 在 TraceSegment 完成时进行调用, 其中最重要实现类为 TraceSegmentServiceClient

TraceSegmentServiceClient 实现了 IConsumer<TraceSegment>, 通过轻量级队列进行消息的消费

private volatile DataCarrier<TraceSegment> carrier;

消费逻辑如下, 通过 GRPC stream调用, 将 TraceSegment 转换为 grpc 用的 SegmentObject 发送到服务端

public void consume(List<TraceSegment> data) {
    if (CONNECTED.equals(status)) {
        final GRPCStreamServiceStatus status = new GRPCStreamServiceStatus(false);
        StreamObserver<SegmentObject> upstreamSegmentStreamObserver = serviceStub.withDeadlineAfter(
            Config.Collector.GRPC_UPSTREAM_TIMEOUT, TimeUnit.SECONDS
        ).collect(new StreamObserver<Commands>() {
            @Override
            public void onNext(Commands commands) {
                ServiceManager.INSTANCE.findService(CommandService.class)
                                       .receiveCommand(commands);
            }

            @Override
            public void onError(
                Throwable throwable) {
                status.finished();
                ...
            }

            @Override
            public void onCompleted() {
                status.finished();
            }
        });

        try {
            for (TraceSegment segment : data) {
                SegmentObject upstreamSegment = segment.transform();
                upstreamSegmentStreamObserver.onNext(upstreamSegment);
            }
        } catch (Throwable t) {
            LOGGER.error(t, "Transform and send UpstreamSegment to collector fail.");
        }

        upstreamSegmentStreamObserver.onCompleted();

        status.wait4Finish();
        segmentUplinkedCounter += data.size();
    } else {
        segmentAbandonedCounter += data.size();
    }

    printUplinkStatus();
}

来源

  • Apache Skywalking 实战
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352