由于我无法弄清楚如何为akka http websocket流程添加错误流程,所以我已经在墙上敲了很长时间。我想要实现的是:
没有错误处理这很容易,但我无法弄清楚如何添加错误。这就是我所拥有的:
type GameDecodeResult =
Either[(String, io.circe.Error), GameLobby.LobbyRequest]
val errorFlow =
Flow[GameDecodeResult]
.mapConcat {
case Left(err) => err :: Nil
case Right(_) => Nil
}
.map { case (message, error) =>
logger.info(s"failed to parse message $message", error)
TextMessage(Error(error.toString).asJson.spaces2)
}
val normalFlow = {
val normalFlowSink =
Flow[GameDecodeResult]
.mapConcat {
case Right(msg) => msg :: Nil
case Left(_) => Nil
}
.map(req => GameLobby.IncomingMessage(userId, req))
.to(Sink.actorRef[GameLobby.IncomingMessage](gameLobby, PoisonPill))
val normalFlowSource: Source[Message, NotUsed] =
Source.actorRef[GameLobby.OutgoingMessage](10, OverflowStrategy.fail)
.mapMaterializedValue { outActor =>
gameLobby ! GameLobby.UserConnected(userId, outActor)
NotUsed
}
.map(outMessage => TextMessage(Ok(outMessage.message).asJson.spaces2))
Flow.fromSinkAndSource(normalFlowSink, normalFlowSource)
}
val incomingMessageParser =
Flow[Message]
.flatMapConcat {
case tm: TextMessage =>
tm.textStream
case bm: BinaryMessage =>
bm.dataStream.runWith(Sink.ignore)
Source.empty }
.map { message =>
decode[GameLobby.LobbyRequest](message).left.map(err => message -> err)
}
这些是我定义的流程,我认为这应该足够好,但我不知道如何组装它们,而且akka流API的复杂性无济于事。这是我试过的:
val x: Flow[Message, Message, NotUsed] =
GraphDSL.create(incomingMessageParser, normalFlow, errorFlow)((_, _, _)) { implicit builder =>
(incoming, normal, error) =>
import GraphDSL.Implicits._
val partitioner = builder.add(Partition[GameDecodeResult](2, {
case Right(_) => 0
case Left(_) => 1
}))
val merge = builder.add(Merge[Message](2))
incoming.in ~> partitioner ~> normal ~> merge
partitioner ~> error ~> merge
}
但不可否认的是,我完全不知道GraphDSL.create
是如何运作的,我可以使用~>
箭头或者我在最后一部分在genreal中所做的事情。它只是不会打字检查,错误信息对我没有帮助。
答案 0 :(得分:1)
在使用GraphDSL构建的Flow中需要修复的一些事项:
无需将3个子流传递给GraphDSL.create
方法,因为这只需要自定义图表的具体化值。您已经确定图表的具体化值为NotUsed
。
使用incoming
运算符连接~>
时,需要将其插座(.out
)连接到分区阶段。
每个GraphDSL定义块都需要返回图形的形状 - 即其外部端口。您可以返回FlowShape
作为输入incoming.in
作为输出,merge.out
作为输出。这些将定义自定义流程的蓝图。
因为最后您想要获得Flow
,所以您错过了最后一次创建调用来自您定义的图表。此致电是Flow.fromGraph(...)
。
下面的代码示例:
val x: Flow[Message, Message, NotUsed] =
Flow.fromGraph(GraphDSL.create() { implicit builder =>
import GraphDSL.Implicits._
val partitioner = builder.add(Partition[GameDecodeResult](2, {
case Right(_) => 0
case Left(_) => 1
}))
val merge = builder.add(Merge[Message](2))
val incoming = builder.add(incomingMessageParser)
incoming.out ~> partitioner
partitioner ~> normalFlow ~> merge
partitioner ~> errorFlow ~> merge
FlowShape(incoming.in, merge.out)
})