Можно ли использовать jaeger для отслеживания сквозной транзакции с опросом и обратным вызовом, используемыми базовыми компонентами, выполняющими шаги транзакции

Я новичок в Jaeger и хотел бы знать, могу ли я отследить сквозную транзакцию с родительским диапазоном и дочерними, подобными описанному ниже, с опросом из дочерних компонентов (без прямого вызова от родительского к дочернему) и обратный вызов от дочерних элементов к родительскому компоненту.

Контекст:

Сначала опишем в упрощенном виде то, что я хотел бы сделать. Решение, состоящее из нескольких компонентов, предоставляет REST API для отправки транзакций. Он синхронно возвращает идентификатор транзакции после вызова и выполняет обратный вызов вызывающей стороне после завершения или сбоя транзакции. Итак, я хочу отследить общую транзакцию от 1 до 3:

  1. invoker вызывает решение REST API с входными параметрами транзакции и URL-адресом обратного вызова
  2. invoker получает идентификатор из ответа REST API
  3. решение вызывает URL-адрес обратного вызова вызывающего с этим идентификатором

Под капотом:

  1. компонент (C1), предоставляющий REST API для отправки транзакции, выполнит некоторые действия, а затем поместит транзакцию в очередь. Это родительский компонент, о котором я упоминал выше.
  2. второй компонент (C2) будет опрашивать C1 с помощью внутреннего REST API для извлечения транзакции из очереди, а затем вызывать REST API, предоставляемый третьим компонентом (C3), для дальнейшего выполнения действий, связанных с транзакцией. В сообщении C1 передает URL-адрес обратного вызова, который затем передается C2 на C3.
  3. третий компонент (C3) проинформирует C1 о том, что он начинает обработку транзакции, вызывая переданный обратный вызов C1 с IN-PROGRESS
  4. а затем C3 организует множество действий, вызывающих синхронно (запрос ‹-> ответ) или асинхронно (запрос ‹-> ответ и затем ‹-callback) различные другие компоненты
  5. Когда все действия завершены успешно или когда одно из них завершается неудачей с неисправимой ошибкой, то компонент C3 снова обратится к C1, чтобы указать УСПЕХ или ОТКАЗ.
  6. Когда C1 вызывается C3, он затем выполняет некоторые закрывающие действия и вызывает обратный вызов инициатора транзакции.

Я предполагаю, что с помощью Jaeger можно будет отследить всю транзакцию, но вот мои вопросы: Вопрос:

  • Q1: Я прав? :-)
  • Q2: Как передать / распространить родительский диапазон, созданный в C1 на C2? (между шагом 1 и 2). Думаю добавить SpanContext на карту в качестве нового атрибута транзакции?
  • Q3: Предполагая, что C2 получил диапазон, он может распространить его на C3, и поскольку C3 будет несколько раз вызывать обратный вызов C1 (шаги 3 и 5), как связать вызов этих обратных вызовов в C1 с родительским диапазоном?

Любые подсказки и подсказки будут приветствоваться. Спасибо.


person fckbo    schedule 31.05.2020    source источник


Ответы (1)


поэтому я попробую, и мой ответ на вопрос выше:

  • Q1) да, это возможно (поздравляем команду jaeger, пакет довольно легко понять с хорошей документацией)

  • Q2) Я немного боролся с этим, и благодаря https://github.com/CHOMNANP/jaeger-js-text-map-demo Я реализовал решение, добавив "textCarrier" с исх. к контексту диапазона, отформатированному как «FORMAT_TEXT_MAP», к сообщению, которое Компонент 1 публиковал для Компонента 2.

Фрагмент кода в C1 при первом вызове API

server.post("/api/vms", (req, res) => {
  console.log('Enter /api/vms');
  const span = tracer.startSpan(req.path);
  // Use the log api to capture a log
  span.log({ event: 'request_received' })
  txSpan = span;
  //console.log("req",span);
  // Use the setTag api to capture standard span tags for http traces
  span.setTag(opentracing.Tags.HTTP_METHOD, req.method)
  span.setTag(opentracing.Tags.SPAN_KIND, opentracing.Tags.SPAN_KIND_RPC_SERVER)
  span.setTag(opentracing.Tags.HTTP_URL, req.path)

за которой следует эта часть при отправке сообщения на Redis:

   const textCarrier = getTextCarrierBySpanObject(span);
   tracer.inject(span.context(), opentracing.FORMAT_TEXT_MAP, textCarrier)

   var vm = req.body;
   console.log('Creating a new vm: ', vm);
   // Publish a message by specifying a channel name.
   try {
      Object.assign(vm, { textCarrier });
      pub.publish('tasks-queue', JSON.stringify(vm));
   } catch(e) {
        console.log("App1 Error when publishing task to App2", e);
   }

Функция getTextCarrierBySpanObject поступает из https://github.com/CHOMNANP/jaeger-js-text-map-demo

function getTextCarrierBySpanObject(_span) {

    const spanContext = _span.context();
    const traceId = spanContext._traceId.toString('hex');
    const spanId = spanContext._spanId.toString('hex');
    let parentSpanId = spanContext._parentId;
    const flag = _.get(spanContext, '_flags', 1);

    if (parentSpanId) {
        parentSpanId = parentSpanId.toString('hex');
    } else {
        parentSpanId = 0;
    }

    const uberTraceId = `${traceId}:${spanId}:${parentSpanId}:${flag}`;
    console.log("uberTraceId===> ", uberTraceId)

    let textCarrier = {
        "uber-trace-id": uberTraceId
    };

    return textCarrier
}

Фрагмент кода в C2, получающий сообщение от Redis

sub.on('message', function(channel, message) {
  // message is json string in our case so we are going to parse it.
  try {
    var json = JSON.parse(message)
    console.log("Task received", message);

    const tracer = opentracing.globalTracer();
    // Extracting the span context from the message
    var parentSpan = tracer.extract(opentracing.FORMAT_TEXT_MAP, JSON.parse(message).textCarrier);
    console.log("textCarrier=",JSON.parse(message).textCarrier);
    const span = tracer.startSpan("/msg", { childOf: parentSpan });
    // Use the log api to capture a log
    span.log({ event: 'msg_received' })

Тестировал с версией 1.13

docker run -d --name jaeger   -e COLLECTOR_ZIPKIN_HTTP_PORT=9411   -p 5775:5775/udp   -p 6831:6831/udp   -p 6832:6832/udp   -p 5778:5778   -p 16686:16686   -p 14268:14268   -p 9411:9411   jaegertracing/all-in-one:1.13
  • Q3) Это довольно просто, используя FORMAT_HTTP_HEADERS для передачи диапазона от компонента C4, вызывающего обратный вызов на C3, и от компонента C3, вызывающего обратный вызов на C1. Единственная «проблема», которую я обнаружил, была скорее «проблема читаемости трассировки», поскольку на самом деле интервалы отображаются в пользовательском интерфейсе Jaeger в «прогрессивном» порядке, а не в «временном порядке», что может немного сбивать с толку ... но экспериментальная функция «графика трассировки» позволила действительно увидеть трассу в правильном порядке, так что все в порядке.

Таким образом, в целом довольно убедительное упражнение по созданию прототипа с Jaeger, скорее всего, позволит испытать его сейчас на реальном проекте, прежде чем опробовать его в производстве.

person fckbo    schedule 01.06.2020