通过侦探进行Hazelcast跟踪

时间:2018-07-25 11:17:13

标签: hazelcast spring-cloud-sleuth

我不知道在榛子糖中是否含有侦探片。在我的应用程序中,我有hazelcast队列,其中为addEntity事件配置了事件侦听器,问题是,一旦此侦听器触发,跨度似乎就被破坏了。我知道ExecutorService集成了侦探,但是com.hazelcast.core.ItemListener是否有类似的东西?预先感谢。

UPD:提供更多详细信息。 我有一些同时使用spring-cloud-sleth和hazelcast队列的示例服务

package com.myapp;

import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IQueue;
import com.hazelcast.core.ItemEvent;
import com.hazelcast.core.ItemListener;
import java.util.concurrent.Executors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.DefaultSpanNamer;
import org.springframework.cloud.sleuth.TraceRunnable;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class SomeService {

private HazelcastInstance hazelcastInstance =
    Hazelcast.newHazelcastInstance();
private IQueue<String> queue = hazelcastInstance.getQueue("someQueue");

private Tracer tracing;


@Autowired(required = false)
public void setTracer(Tracer tracer) {
    this.tracing = tracer;
}


{
    queue.addItemListener(new ItemListener<String>() {
    @Override
    public void itemAdded(ItemEvent<String> item) {
        log.info("This is span");
        log.info("This is item " + item);
    }

    @Override
    public void itemRemoved(ItemEvent<String> item) {
    }
    }, true);
}

@Async
public void processRequestAsync() {
    log.info("Processing async");
    log.info("This is span");
    Executors.newSingleThreadExecutor().execute(
        new TraceRunnable(tracing, new DefaultSpanNamer(), () -> log.info("Some Weird stuff")));
    queue.add("some stuff");
}

}

,一旦我调用processRequestAsync,我就会在控制台中收到以下输出:

INFO [-,792a6c3ad3e91280,792a6c3ad3e91280,false] 9996 --- [nio-8080-exec-2] com.myapp.SomeController            : Incoming request!
INFO [-,792a6c3ad3e91280,792a6c3ad3e91280,false] 9996 --- [nio-8080-exec-2] com.myapp.SomeController            : This is current span [Trace: 792a6c3ad3e91280, Span: 792a6c3ad3e91280, Parent: null, exportable:false]
INFO [-,792a6c3ad3e91280,7d0c06d3e24a7ba1,false] 9996 --- [cTaskExecutor-1] com.myapp.SomeService               : Processing async
INFO [-,792a6c3ad3e91280,7d0c06d3e24a7ba1,false] 9996 --- [cTaskExecutor-1] com.myapp.SomeService               : This is span
INFO [-,792a6c3ad3e91280,8a2f0a9028f44979,false] 9996 --- [pool-1-thread-1] com.myapp.SomeService               : Some Weird stuff
INFO [-,792a6c3ad3e91280,7d0c06d3e24a7ba1,false] 9996 --- [cTaskExecutor-1] c.h.i.p.impl.PartitionStateManager       : [10.236.31.22]:5701 [dev] [3.8.3] Initializing cluster partition table arrangement...
INFO [-,,,] 9996 --- [e_1_dev.event-4] com.myapp.SomeService               : This is span
INFO [-,,,] 9996 --- [e_1_dev.event-4] com.myapp.SomeService               : This is item ItemEvent{event=ADDED, item=some stuff, member=Member [10.236.31.22]:5701 - b830dbf0-0977-42a3-a15d-800872221c84 this} 

所以一旦进入eventListener代码,跨度似乎就坏了,我想知道如何在hazelcast队列中传播或创建新跨度

2 个答案:

答案 0 :(得分:1)

Sleuth(在撰写本文时)不支持Hazelcast。

该解决方案不仅限于Hazelcast,还更通用-您需要在客户端和服务器之间传递Zipkin的brave.Span,但是brave.Span无法实现序列化。

Zipkin提供了解决此问题的方法。

鉴于客户端上的brave.Span,您可以将其转换为java.util.Map

Span span = ...
Map<String, String> map = new HashMap<>();

tracing.propagation().injector(Map<String, String>::put).inject(span.context(), map);

在服务器上,您可以将java.util.Map转换回brave.Span

Span span = tracer.toSpan(tracing.propagation().extractor(Map<String, String>::get).extract(map).context())

显然可以根据需要替换java.util.Map的用法,但是原理是相同的。

答案 1 :(得分:0)

我无法让它用于ItemListeners。我认为我们需要能够将Hazelcast的StripedExecutor包装在类似LazyTraceThreadPoolTask​​Executor的东西中(但可以接受普通的Executor委托而不是ThreadPoolTask​​Executor)。

对于EntryProcessor,我已经一起破解了。创建EntryProcessor的工厂,传入创建处理器的线程的当前跨度。当处理器运行时,它将使用该范围作为执行程序线程中的父范围。

@Component
public class SleuthedEntryProcessorFactory {

    private final Tracer tracer;

    public SleuthedEntryProcessorFactory(Tracer tracer) {
        this.tracer = tracer;
    }

    /**
     * Create an entry processor that will continue the Sleuth span of the thread 
     * that invokes this method.
     * Mutate the given value as required. It will then be set on the entry.
     *
     * @param name name of the span
     * @param task task to perform on the map entry
     */
    public <K, V, R> SleuthedEntryProcessor<K, V, R> create(String name, Function<V, R> task) {
        return new SleuthedEntryProcessor<>(name, tracer.getCurrentSpan(), task);
    }
}

/**
 * Copies the MDC context (which contains Sleuth's trace ID, etc.) and the current span
 * from the thread that constructs this into the thread that runs this.

 * @param <K> key type
 * @param <V> value type
 * @param <R> return type
 */
@SpringAware
public class SleuthedEntryProcessor<K, V, R> extends AbstractEntryProcessor<K, V> {

    private final Map<String, String> copyOfContextMap;
    private final String name;
    private final Span parentSpan;
    private final Function<V, R> task;
    private transient Tracer tracer;

    public SleuthedEntryProcessor(String name, Span parentSpan, Function<V, R> task) {
        this(name, parentSpan, task, true);
    }

    public SleuthedEntryProcessor(
            String name, Span parentSpan, Function<V, R> task, boolean applyOnBackup) {
        super(applyOnBackup);
        this.name = name + "Hz";
        this.parentSpan = parentSpan;
        this.task = task;
        copyOfContextMap = MDC.getCopyOfContextMap();
    }

    @Override
    public final R process(Map.Entry<K, V> entry) {
        if (nonNull(copyOfContextMap)) {
            MDC.setContextMap(copyOfContextMap);
        }

        Span span = tracer.createSpan(toLowerHyphen(name), parentSpan);
        try {
            V value = entry.getValue();
            // The task mutates the value.
            R result = task.apply(value);
            // Set the mutated value back onto the entry.
            entry.setValue(value);
            return result;

        } finally {
            MDC.clear();
            tracer.close(span);
        }
    }

    @Autowired
    public void setTracer(Tracer tracer) {
        this.tracer = tracer;
    }
}

然后将EntryProcessor传递给您的IMap,如下所示:

Function<V, R> process = ...;
SleuthedEntryProcessor<K, V, R> entryProcessor = sleuthedEntryProcessorFactory.create(label, process);
Map<K, R> results = iMap.executeOnEntries(entryProcessor);