如何通过退避访问 Alpakka CommittableSource 的指标?

How to access metrics of Alpakka CommittableSource with back off?

访问 Alpakka PlainSource 的指标似乎相当简单,但我如何才能对 CommittableSource 做同样的事情?

我目前有一个简单的消费者,像这样:

class Consumer(implicit val ma: ActorMaterializer, implicit val ec: ExecutionContext) extends Actor {

  private val settings = ConsumerSettings(
    context.system,
    new ByteArrayDeserializer,
    new StringDeserializer)
    .withProperties(...)

  override def receive: Receive = Actor.emptyBehavior

  RestartSource
    .withBackoff(minBackoff = 2.seconds, maxBackoff = 20.seconds, randomFactor = 0.2)(consumer)
    .runForeach { handleMessage }

  private def consumer() = {
    AkkaConsumer
      .committableSource(settings, Subscriptions.topics(Set(topic)))
      .log(getClass.getSimpleName)
      .withAttributes(ActorAttributes.supervisionStrategy(_ => Supervision.Resume))
  }

  private def handleMessage(message: CommittableMessage[Array[Byte], String]): Unit = {
    ...
  }
}

在这种情况下,我如何才能访问消费者指标?

我们正在使用 Java prometheus client,我使用直接从 JMX 获取指标的自定义收集器解决了我的问题:

import java.lang.management.ManagementFactory
import java.util

import io.prometheus.client.Collector
import io.prometheus.client.Collector.MetricFamilySamples
import io.prometheus.client.CounterMetricFamily
import io.prometheus.client.GaugeMetricFamily
import javax.management.ObjectName

import scala.collection.JavaConverters._
import scala.collection.mutable

class ConsumerMetricsCollector(val labels: Map[String, String] = Map.empty) extends Collector {
  val metrics: mutable.Map[String, MetricFamilySamples] = mutable.Map.empty

  def collect: util.List[MetricFamilySamples] = {
    val server = ManagementFactory.getPlatformMBeanServer
    for {
      attrType <- List("consumer-metrics", "consumer-coordinator-metrics", "consumer-fetch-manager-metrics")
      name <- server.queryNames(new ObjectName(s"kafka.consumer:type=$attrType,client-id=*"), null).asScala
      attrInfo <- server.getMBeanInfo(name).getAttributes.filter { _.getType == "double" }
    } yield {
      val attrName = attrInfo.getName
      val metricLabels = attrName.split(",").map(_.split("=").toList).collect {
        case "client-id" :: (id: String) :: Nil => ("client-id", id)
      }.toList ++ labels

      val metricName = "kafka_consumer_" + attrName.replaceAll(raw"""[^\p{Alnum}]+""", "_")
      val labelKeys = metricLabels.map(_._1).asJava

      val metric = metrics.getOrElseUpdate(metricName,
        if(metricName.endsWith("_total") || metricName.endsWith("_sum")) {
          new CounterMetricFamily(metricName, attrInfo.getDescription, labelKeys)
        } else {
          new GaugeMetricFamily(metricName, attrInfo.getDescription, labelKeys)
        }: MetricFamilySamples
      )

      val metricValue = server.getAttribute(name, attrName).asInstanceOf[Double]
      val labelValues = metricLabels.map(_._2).asJava
      metric match {
        case f: CounterMetricFamily => f.addMetric(labelValues, metricValue)
        case f: GaugeMetricFamily => f.addMetric(labelValues, metricValue)
        case _ =>
      }
    }
    metrics.values.toList.asJava
  }
}