You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算Tapir + Akka HTTP的输出消息大小?

获取Tapir + Akka HTTP端点的输出消息大小指标

针对你的场景(仅返回Circe编码的简单字符串),可以通过以下两种方式实现输出消息大小的指标收集:

方式一:在Tapir指标观察者中直接计算编码后大小

利用Tapir的ServerEndpointMetrics扩展自定义指标逻辑,在响应完成时拿到返回值,通过Circe编码为字符串后计算UTF-8字节长度:

import sttp.tapir.server.ServerEndpointMetrics
import sttp.tapir.model.ServerResponse
import io.circe.syntax._
import scala.concurrent.Future

class CustomTapirMetrics extends ServerEndpointMetrics[Future] {
  override def onComplete[I, E, O](endpoint: sttp.tapir.server.ServerEndpoint[I, E, O, Any, Future], response: ServerResponse[O]): Future[Unit] = {
    // 计算输出大小
    response.body.foreach { outputValue =>
      // 用Circe将输出序列化为无空格JSON字符串
      val jsonStr = outputValue.asJson.noSpaces
      // 计算UTF-8编码后的字节数
      val outputSize = jsonStr.getBytes("UTF-8").length
      
      // 这里替换为你的指标上报逻辑(比如Prometheus计数器/直方图)
      println(s"Endpoint ${endpoint.endpoint.name} output size: $outputSize bytes")
    }

    // 保留原有指标逻辑(状态码、耗时等)
    Future.unit
  }

  // 实现其他必需的方法
  override def onStart[I, E, O](endpoint: sttp.tapir.server.ServerEndpoint[I, E, O, Any, Future]): Future[Unit] = Future.unit
}

创建Tapir Akka HTTP服务器时传入自定义指标:

import sttp.tapir.server.akkahttp.AkkaHttpServerOptions

val serverOptions = AkkaHttpServerOptions.custom
  .metrics(new CustomTapirMetrics)
  .build

val route = TapirAkkaHttpServer.toRoute(yourEndpoints)(serverOptions)

方式二:通过Akka HTTP路由拦截器计算响应体大小

从Akka HTTP层面拦截响应,直接读取响应体长度:

import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.model.HttpResponse
import akka.util.ByteString

val tapirRoute = TapirAkkaHttpServer.toRoute(yourEndpoints)(serverOptions)
val routeWithSizeMetrics = tapirRoute.mapResponse { response =>
  // 优先读取Content-Length头
  val contentLength = response.entity.contentLengthOption match {
    case Some(len) => len
    // 非流式响应可转为ByteString计算长度
    case None => response.entity.dataBytes.runFold(ByteString.empty)(_ ++ _).map(_.length)
  }

  // 上报contentLength到指标系统(异步场景需结合Akka调度)
  contentLength.foreach(size => println(s"Response size: $size bytes"))

  response
}

针对你的EndpointOutput结构优化

从你打印的EndpointOutput来看,输出是Pair(Empty, Body(StringBody(UTF-8), ...)),可针对性匹配StringBody类型处理:

import sttp.tapir.EndpointOutput

// 匹配StringBody类型的输出
output match {
  case sttp.tapir.Pair(_, sttp.tapir.Body(sttp.tapir.StringBody(_), _, _), _, _) =>
    response.body.foreach { str: String =>
      val size = str.getBytes("UTF-8").length
      // 上报指标
    }
  case _ => // 其他输出类型的降级处理
}

内容的提问来源于stack exchange,提问作者Matzz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 14:52:25