使用flink对链路数据进行采样

1,796 阅读2分钟

现在开源框架采用的采样策略一般是采用类header向下传递采样标记,这种情况下有以下几个问题。

1、当中间链路节点出现需要采样的情况时,req header向后传递采样标志,rsp header向前传递采样标志。这种在异步调用情况下,rsp header向前传递会失效。

2、采用这种标记的只能在单条链路上有效,异常节点之前出现分叉链路情况的时候,分叉的另外链路无法打上采样标记。 链路采样.png

使用flink对链路进行采样,可以根据traceId作为key,采用sessionTime Windows作为窗口,保证单个链路数据的span能在sessionTime内搜集到flink的source源中。

基于flink的窗口:

1、单某个节点的数据符合采样的需求,将这个窗口的链路数据置为采样,并将这个异常节点放到窗口的异常列表中(单独存储,便于搜索)。

2、从整个链路中获取头节点(链路的搜索一般是搜索头结点的数据,比如api请求发起的整个链路。

当sessionTime超过预设的时间,我们可以假定整体链路已经处理完毕,窗口进入处理方法中,我们可以将以上分类的数据发送到sink(kafka producer)中。

后续的处理节点可以将分类数据中的首节点和异常节点放入es中,便于对关键字段进行搜索;全部数据存储到hbase中,通过traceid生成rowkey,spanid和节点的server/client端生成列族id。从es中搜索到traceid,然后通过traceid从hbase中搜索到全链路的数据。

实现中需要解决的问题:

1、traceid除了在同一链路中,其他时间段是不会重复出现的,通过traceid作为flink的窗口的key,当这个链路处理完成之后,flink的窗口状态不会清空,当flink运行一段时间之后,会导致堆内存耗尽,需要增加自定义的trigger实现,processfunction处理完成之后抛弃traceid对应的状态。

 public static Trigger<Span, TimeWindow> getTrigger() {
        return new Trigger<Span, TimeWindow>() {
            private int flag = 0;

            @Override
            public TriggerResult onElement(Span span, long l, TimeWindow timeWindow, TriggerContext triggerContext) throws Exception {
                triggerContext.registerProcessingTimeTimer(timeWindow.maxTimestamp());
                if(flag > 99){
                    flag = 0;
                    return TriggerResult.FIRE_AND_PURGE;
                }else{
                    flag++;
                }
                return TriggerResult.CONTINUE;
            }

            @Override
            public TriggerResult onProcessingTime(long l, TimeWindow timeWindow, TriggerContext triggerContext) throws Exception {
                return TriggerResult.FIRE_AND_PURGE;
            }

           ...
        };
    }

2、依赖链路收集具有较好的实时性,flink的source源需尽量保证通traceid的所有span链路数据能在一段可接受的sessionTime内能被收集起来。

3、链路数据qps很高,需要的flink资源会比较多,推荐数据源能保证数据的时间顺序,这样flink使用eventTime能将内存的使用维持在一个比较平稳的状态。