Pull to refresh

Трассировка сервисов через очередь сообщений. OpenTelemetry, NATS

Level of difficultyHard
Reading time4 min
Views4.7K

Это небольшой гайд о том, как обеспечить наблюдаемость в вашей событийно-ориентированной облачной системе.

Немного теории

Cloud application

Облачные технологии позволяют организациям создавать и запускать масштабируемые приложения в современных динамических окружениях, таких как общедоступные, частные и гибридные облака…

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

– Cloud Native Computing Foundation (CNCF)

Согласно этому определению, облачные приложения – больше, чем просто приложения, которые действуют в облаке. Они также масштабируемы, слабо связаны, устойчивы, управляемы и доступны для наблюдения. Можно сказать, что наличие этих «облачных атрибутов» позволяет называть системы облачными.

Event-driven architecture (EDA)

Архитектура, управляемая событиями, способствует слабой взаимозависимости между компонентами системы, что приводит к большей гибкости. Микросервисы могут масштабироваться независимо, выходить из строя без влияния на другие сервисы и снижать сложность рабочих процессов.

Observability

Наблюдаемость системы – это мера простоты определения ее внутреннего состояния по наблюдаемым результатам. Система считается наблюдаемой, если можно быстро и последовательно получать ответы на все новые вопросы о ней с минимальными предварительными знаниями, без необходимости внедряться в существующий код или писать новый.

OpenTelemetry

OpenTelemetry, также известный как OTel, представляет собой открытый стандарт CNCF, который обеспечивает распределенную трассировку и сбор метрик из ваших приложений.

Trace Context

Это метаданные о span-ах в трассировке. Например, предположим, что служба A вызывает службу B, и вы хотите отследить вызов. В этом случае OpenTelemetry будет использовать Trace Context для захвата идентификатора трассировки и текущего span-а из службы A, чтобы span-ы, созданные в службе B, могли подключаться и добавляться к трассировке.

Это известно как распространение контекста.

Context Propagation

Распространение контекста - это основная концепция, которая обеспечивает распределенную трассировку. При распространении контекста span-ы могут быть соотнесены друг с другом и собраны в трассировку, независимо от того, где они генерируются. OpenTelemetry определяет распространение контекста с помощью двух субконцептов: Context и Propagation.

Context - это объект, который содержит информацию для отправляющей и принимающей служб, позволяющую соотнести один span с другим и связать его с трассировкой в целом.

Propagation - это механизм, который перемещает контекст между службами и процессами. Поступая таким образом, он собирает распределенную трассировку. Он сериализует или десериализует Span Context и предоставляет соответствующую информацию трассировки для распространения от одной службы к другой.

На практике

Хорошо, давайте создадим экземпляр propagation и инициализируем его:

import (
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/propagation"
)

tc := propagation.TraceContext{}
// Register the TraceContext propagator globally.
otel.SetTextMapPropagator(tc)

В сервисе A, контекст которого мы хотим передать:

// GetTextMapPropagator returns the global TextMapPropagator.
prop := otel.GetTextMapPropagator()
// HeaderCarrier adapts http.Header to satisfy the TextMapCarrier interface.
headers := make(propagation.HeaderCarrier)
prop.Inject(ctx, headers)

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

В сервисе B, в котором мы хотим получить контекст:

var headers propagation.HeaderCarrier
// we get the headers and convert them to HeaderCarrier...
prop := otel.GetTextMapPropagator()
// Extract reads cross-cutting concerns from the carrier into a Context.
ctx = prop.Extract(ctx, headers)

NATS

// Simple Async Subscriber
nc.Subscribe("foo", func(m *nats.Msg) {
    fmt.Printf("Received a message: %s\n", string(m.Data))
})

// Header represents the optional Header for a NATS message,
// based on the implementation of http.Header.
type Header map[string][]string

// Msg represents a message delivered by NATS. This structure is used
// by Subscribers and PublishMsg().
type Msg struct {
	Header  Header
}

нетрудно заметить что propagation.HeaderCarrier и nats.Header основан на реализации http.Header. Поэтому, чтобы скопировать данные из одной структуры в другую, я воспользовался реализацией http.Header.Clone()

В заключении

Всё достаточно просто, если немного почитать документацию. Было очень сложно подобрать русские термины, под устоявшиеся английские определения, поэтому большую часть я просто не стал переводить, что бы не ввести вас в заблуждение.

Полный код проекта доступен в моем репозитории - nats-tracing.

Так же при написании гайда я использовал открытые источники:

Tags:
Hubs:
Total votes 5: ↑1 and ↓4-3
Comments10

Articles