Трассировка в Akka при помощи библиотеки Kamon

17 декабря 2015

Ранее в заметке Делаем метрики и мониторинг для Akka при помощи Kamon мы уже познакомились с некоторыми возможностями Kamon. Правда, нельзя сказать, что возможности эти были особо впечатляющими, так как метрики можно вполне успешно писать и безо всякого Kamon. Сегодня же мы познакомимся с некоторыми другими средствами, предоставляемыми этой несколько спорной, но определенно очень мощной библиотекой.

Первая довольно часто встречающаяся на практике задача — неявное пробрасывание некоторого контекста между акторами. Допустим, актор A1 проставляет некоторый контест, например, информацию о пользователе, запросе, с которым он пришел в бэкенд, а также, возможно, некоторых параметрах этого запроса. Затем актор A1 шлет запрос A2, тот шлет A3, и так далее, до A42. Отметим, что если в проекте используется Akka Cluster, акторы эти могут быть на очень даже разных машинах. В какой-то момент мы решили, что в акторе A42 неплохо бы получить информацию о контексте, полученном в акторе A1. Можно, конечно, пробрасывать этот контекст явно во всех сообщениях между акторами. Или менее явно, если воспользоваться имплиситами. Но для этого нам понадобится поправить код как минимум в 42 местах! А возможно и больше, так как акторы могут обмениваться несколькими типам сообщений.

К счастью, с помощью Kamon эту проблему можно решить намного проще.

В акторе A1 пишем что-то вроде:

Tracer.withNewContext(
  "TestContext",
  traceToken = Some(s"dummy-trace-token"),
  autoFinish = true) {
    // ... тут обычный код с запросами к остальным акторам ...
}

Теперь в акторе A42 можно написать:

val token = Tracer.currentContext.token

… и получить текущий контекст, в данном случае строчку «dummy-trace-token». Понятно, что в общем случае нам хотелось бы передавать что-то чуть более интересное, чем обычные строки. Это легко реализовать, создав новый case class с поддержкой сериализации в JSON. Работает такое пробрасывание контекста на магии AspectJ, поэтому в IntelliJ IDEA на забудьте в Run → Edit Configurations… → VM Options прописать -javaagent:/path/to/aspectjweaver-1.8.2.jar. Также этот параметр требуется передавать JVM при запуске приложения в продакте, поэтому не забудьте поправить wrapper.conf или что вы там используете в проекте.

Не правда ли, приведенное выше решение лучше, чем внесение правок в 42 местах?

Рассмотрим еще пример. Когда в большом (и особенно — распределенном!) проекте что-то где-то начинает тормозить, определить, что именно, где именно и почему тормозит не всегда бывает просто. Kamon помогает нам и здесь.

Определим такой вот актор для сборки трейсов:

package me.eax.examples.tracing

import akka.actor.{ActorLogging, Actor}
import kamon.trace.TraceInfo

class TraceSubscriber extends Actor with ActorLogging {
  var traceNumber = 0
  override def receive: Receive = {
    case inf: TraceInfo =>
      traceNumber = traceNumber + 1
      val segmentsNumber = inf.segments.size
      log.info(
        s"""|Trace number $traceNumber ---
            |Trace Name: ${inf.name},
            |timestamp: ${inf.timestamp},
            |elapsedTime: ${inf.elapsedTime},
            |segmentsNumber: $segmentsNumber
            |"""
.stripMargin.replace('\n', ' ')
      )
  }
}

Подпишем его на трейсы:

val traceSubscriber = {
  system.actorOf(Props(new TraceSubscriber), "traceSubscriber")
}
Kamon.tracer.subscribe(traceSubscriber)

Теперь внутри блока Tracer.withNewContext (см выше) мы можем определять так называемые «сегменты»:

Tracer.currentContext.withNewAsyncSegment(
  "Main/sayHello",
  "ask",
  "me.eax.example") {
    // ... тут обычный код ...
}

При выходе из блока Tracer.withNewContext в актор traceSubscriber магическим образом будет прилетать информация обо всех сегментах, выполненных в рамках контекста. Помимо информации, используемой в примере кода выше, можно также получить данные и о каждом отдельном сегменте, включая время, которое этот сегмент выполнялся:

Трассировка в Akka при помощи Kamon

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

Полную версию исходного кода к этой заметке можно найти на GitHub. Обратите особое внимание на использование плагина sbt-aspectj, подкладывание файла META-INF/aop.xml, а также настройки проекта в build.sbt. Следует понимать, что этот пост поверхностно описывает сравнительно простой пример, а больше подробной информации о представленном выше функционале вы найдете на официальном сайте Kamon.

А используете ли вы в своих проектах описанные возможности Kamon?

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.