Отправить TraceId между потоками

У нас есть распределенное приложение, основанное на микросервисной архитектуре. В одном из наших микросервисов мы следуем шаблону производитель-потребитель.

Производитель получает запросы, сохраняет их в базе данных, помещает запрос в BlockingQueue и отправляет ответ обратно клиенту. Потребитель, работающий в отдельном потоке, прослушивает очередь блокировки. В тот момент, когда он получает объект запроса, он выполняет над ним определенные операции.

Запрос, полученный производителем, сохраняется в базе данных асинхронно с использованием CompleteableFutures.

Проблема здесь в том, как перенаправить TraceId в методы, обрабатывающие requestObject внутри потребительского потока. Поскольку поток-потребитель может обрабатывать эти объекты намного позже, после отправки ответа потребителю.

Также как перенаправить traceId через асинхронные вызовы?

Спасибо


person Apurv    schedule 30.12.2017    source источник


Ответы (1)


Это интересный вопрос. Я думаю, что вы можете сохранить запрос вместе с его заголовками. Затем на стороне потребителя вы можете использовать интерфейс SpanExtractor так же, как мы делаем здесь — https://github.com/spring-cloud/spring-cloud-sleuth/blob/v1.3.0.RELEASE./spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java#L351 (Span parent = spanExtractor().joinTrace(new HttpServletRequestTextMap(request));). Это означает, что из HttpServletRequest мы извлекаем значения для построения диапазона. Затем, как только вы получили Span, вы можете просто использовать метод Tracer#continueSpan(Span) перед обработкой, а затем Tracer#detach(Span) в блоке finally. Например.

Span parent = spanExtractor().joinTrace(new HttpServletRequestTextMap(request));
try {
   tracer.continueSpan(parent);
   // do whatever you need
} catch(Exception e) {
  tracer.addTag("error", doSthWithTheExceptionMsg(e));
} finally {
  tracer.detach(parent); 
}
person Marcin Grzejszczak    schedule 03.01.2018
comment
Мы также попробуем сделать это, но сейчас мы сохраняем traceId, а затем в потоке-потребителе делаем вызов: — Span parent = Span.builder().traceId(Span.hexToId(traceId)).build(); tracer.createSpan("ConsumerSpan", parent); Видите ли вы какие-либо недостатки в этом подходе? - person Apurv; 08.01.2018
comment
более того, потребитель снова порождает несколько потоков, поскольку все вызовы внутри потребителя являются асинхронными, каков правильный подход к этому? - person Apurv; 08.01.2018
comment
На мой взгляд выглядит нормально. Вы наблюдаете какие-либо проблемы? - person Marcin Grzejszczak; 08.01.2018
comment
Пока нет, спасибо :) - person Apurv; 08.01.2018
comment
Привет, Марцин. Используя этот подход, каждый раз, когда я делаю http-запрос с использованием restTemplate, создается новый TraceId, а не пересылается тот же TraceId, и я не получаю x-b3-traceId в качестве заголовка ответа. - person Apurv; 30.01.2018
comment
Является ли ваш RestTemplate компонентом? Он генерируется на стороне клиента или на стороне сервера? Если это не bean-компонент, то он будет сгенерирован на стороне сервера, поскольку клиентская сторона не будет инструментирована. - person Marcin Grzejszczak; 30.01.2018
comment
да, это bean-компонент, и он создается на стороне сервера. - person Apurv; 30.01.2018
comment
Думаю, без образца вам будет сложно помочь. - person Marcin Grzejszczak; 30.01.2018
comment
Привет! Устранена проблема путем передачи traceableExecutorPool в CompletableFuture.supplyAsync. Поскольку restTemplate выполнялся в новом потоке, с которым не был связан traceId. Новый TraceId создавался для каждого вызова restTemplate.exchange. CompletableFuture.supplyAsync(() -> restTemplate.exchange(requestEntity, String.class), traceableExecutorService) Может кому пригодится :) - person Apurv; 31.01.2018
comment
Этот пример доступен в документации. Тот, что с обёрткой callable через Traceable ES. В любом случае, я рад, что у вас все работает! Можем ли мы пометить это как ответ? - person Marcin Grzejszczak; 31.01.2018