如何计算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
相关产品推荐
相关产品推荐

