Scala 如何衡量Akka WebSocket流的吞吐量?
我是Akka的新手,开发了一个示例Akka WebSocket服务器,它使用Scala 如何衡量Akka WebSocket流的吞吐量?,scala,akka,akka-stream,akka-http,Scala,Akka,Akka Stream,Akka Http,我是Akka的新手,开发了一个示例Akka WebSocket服务器,它使用BroadcastHub(基于来自Akka的示例)将文件内容流式传输到客户端 假设客户端的消耗速度与服务器的消耗速度一样快,我如何测量吞吐量(消息/秒) // file source val fileSource = FileIO.fromPath(Paths.get(path) // Akka file source val theFileSource = fileSource .toMat(BroadcastH
BroadcastHub
(基于来自Akka的示例)将文件内容流式传输到客户端
假设客户端的消耗速度与服务器的消耗速度一样快,我如何测量吞吐量(消息/秒)
// file source
val fileSource = FileIO.fromPath(Paths.get(path)
// Akka file source
val theFileSource = fileSource
.toMat(BroadcastHub.sink)(Keep.right)
.run
//Akka kafka file source
lazy val kafkaSourceActorStream = {
val (kafkaSourceActorRef, kafkaSource) = Source.actorRef[String](Int.MaxValue, OverflowStrategy.fail)
.toMat(BroadcastHub.sink)(Keep.both).run()
Consumer.plainSource(consumerSettings, Subscriptions.topics("perf-test-topic"))
.runForeach(record => kafkaSourceActorRef ! record.value().toString)
}
def logicFlow: Flow[String, String, NotUsed] = Flow.fromSinkAndSource(Sink.ignore, theFileSource)
val websocketFlow: Flow[Message, Message, Any] = {
Flow[Message]
.collect {
case TextMessage.Strict(msg) => Future.successful(msg)
case _ => println("ignore streamed message")
}
.mapAsync(parallelism = 2)(identity)
.via(logicFlow)
.map { msg: String => TextMessage.Strict(msg) }
}
val fileRoute =
path("file") {
handleWebSocketMessages(websocketFlow)
}
}
def startServer(): Unit = {
bindingFuture = Http().bindAndHandle(wsRoutes, HOST, PORT)
log.info(s"Server online at http://localhost:9000/")
}
def stopServer(): Unit = {
bindingFuture
.flatMap(_.unbind())
.onComplete{
_ => system.terminate()
log.info("terminated")
}
}
//ws client
def connectToWebSocket(url: String) = {
println("Connecting to websocket: " + url)
val (upgradeResponse, closed) = Http().singleWebSocketRequest(WebSocketRequest(url), websocketFlow)
val connected = upgradeResponse.flatMap{ upgrade =>
if(upgrade.response.status == StatusCodes.SwitchingProtocols )
{
println("Web socket connection success")
Future.successful(Done)
}else {
println("Web socket connection failed with error: {}", upgrade.response.status)
throw new RuntimeException(s"Web socket connection failed: ${upgrade.response.status}")
}
}
connected.onComplete { msg =>
println(msg)
}
}
def websocketFlow: Flow[Message, Message, _] = {
Flow.fromSinkAndSource(printFlowRate, Source.maybe)
}
lazy val printFlowRate =
Flow[Message]
.alsoTo(fileSink("output.txt"))
.via(flowRate(1.seconds))
.to(Sink.foreach(rate => println(s"$rate")))
def flowRate(sampleTime: FiniteDuration) =
Flow[Message]
.conflateWithSeed(_ ⇒ 1){ case (acc, _) ⇒ acc + 1 }
.zip(Source.tick(sampleTime, sampleTime, NotUsed))
.map(_._1.toDouble / sampleTime.toUnit(SECONDS))
def fileSink(file: String): Sink[Message, Future[IOResult]] = {
Flow[Message]
.map{
case TextMessage.Strict(msg) => msg
case TextMessage.Streamed(stream) => stream.runFold("")(_ + _).flatMap(msg => Future.successful(msg))
}
.map(s => ByteString(s + "\n"))
.toMat(FileIO.toFile(new File(file)))(Keep.right)
}
在我上次工作的地方,我实施了这种性质的绩效基准 基本上,这意味着创建一个简单的客户端应用程序,它使用来自websocket的消息并输出一些指标。自然的选择是使用akka http客户端对WebSocket的支持来实现客户端。见: 然后,我们使用千分尺库向普罗米修斯公开度量,普罗米修斯是我们报告和绘制图表的首选工具
您可以将吞吐量测量流附加到现有流。以下是一个受启发的示例,它打印每秒从上游源发出的整数数:
val rateSink = Flow[Int]
.conflateWithSeed(_ => 0){ case (acc, _) => acc + 1 }
.zip(Source.tick(1.second, 1.second, NotUsed))
.map(_._1)
.toMat(Sink.foreach(i => println(s"$i elements/second")))(Keep.right)
在下面的示例中,我们将上面的接收器连接到发出1到1000万整数的源。为了防止速率测量流干扰主流(在本例中,主流仅将每个整数转换为字符串并返回作为物化值一部分处理的最后一个字符串),我们使用:
运行上述示例将打印如下内容:
0 elements/second
2597548 elements/second
3279052 elements/second
mainFut completed: 10000000
3516141 elements/second
607254 elements/second
rateFut completed: Done
val websocketFlow: Flow[Message, Message, Any] = {
Flow[Message]
.wireTap(rateSink) // <---
.collect {
case TextMessage.Strict(msg) => Future.successful(msg)
case _ => println("ignore streamed message")
}
.mapAsync(parallelism = 2)(identity)
.via(logicFlow)
.map { msg: String => TextMessage.Strict(msg) }
}
如果不需要对rateSink
的物化值进行引用,请使用wireTap
而不是wireTapMat
。例如,将rateSink
附加到WebSocket流可能如下所示:
0 elements/second
2597548 elements/second
3279052 elements/second
mainFut completed: 10000000
3516141 elements/second
607254 elements/second
rateFut completed: Done
val websocketFlow: Flow[Message, Message, Any] = {
Flow[Message]
.wireTap(rateSink) // <---
.collect {
case TextMessage.Strict(msg) => Future.successful(msg)
case _ => println("ignore streamed message")
}
.mapAsync(parallelism = 2)(identity)
.via(logicFlow)
.map { msg: String => TextMessage.Strict(msg) }
}
val-websocketFlow:Flow[消息,消息,任意]={
流[消息]
.wireTap(rateSink)//Future.successful(msg)
案例=>println(“忽略流式消息”)
}
.mapAsync(并行度=2)(标识)
.via(逻辑流)
.map{msg:String=>TextMessage.Strict(msg)}
}
wireTap
是在Source
和Flow
相关提示上定义的,您构建文件源的方式效率非常低(并且会泄漏文件句柄)。请使用akka docsThanks中记录的FileIO对象中的内置文件源,以获得宝贵的反馈。我使用web套接字客户端更新了代码,该客户端可以测量速率(事件/秒)并写入文件。我正在使用akka kafka流源访问消费者数据并发送到web套接字客户端。我得到的最大速率是35k/秒,考虑到使用akka kafka plain consumer的基准是944746 mgs/秒,这似乎很低。这是Benchmark链接,我还需要做什么来提高吞吐量?感谢您的反馈。