我一直在使用Akka应用程序。 95%的代码是用纯粹的演员编写的。现在我要将应用程序的某些部分移动到Akka Streams。 让我了解以下逻辑如何看待Akka Streams:
+------------+
| CreateUser |
+------------+
|
|
+------------+ +-------------------+
| CheckEmail |-----|EmailIsAlreadyInUse|
+------------+ +-------------------+
|
|
+------------+ +-------------------+
|3rdPartyCall|-----|NoUserInInternalDB |
+------------+ +-------------------+
|
|
+------------+ +-------------------+
| SaveUser |-----| UserDBError |
+------------+ +-------------------+
|
|
+------------+
| UserSaved |
+------------+
在当前实现中,所有块都是我发送给适当的actor的消息。如果消息流成功,我会向发件人发送UserSaved
消息。否则,我会将一封验证邮件发送给发件人:EmailIsAlreadyInUse
或NoUserInInternalDB
或UserDBError
。
以下是一组消息:
case class CreateUser(email: String)
case class CheckEmailUniqueness(email: String)
case class ExternalServiceValidation(email: String)
case class SaveUser(email: String)
sealed trait CreateUserResult
sealed trait CreateUserError
case class UserCreated(email: String) extends CreateUserResult
case class EmailIsAlreadyInUse(email: String) extends CreateUserResult with CreateUserError
case class NoUserInExternalDB(email: String) extends CreateUserResult with CreateUserError
case class UserDBError(email: String) extends CreateUserResult with CreateUserError
如何将此逻辑迁移到Akka Streams?
答案 0 :(得分:3)
讯息结构
因为akka-stream数据是在一个方向上发送的,从源到接收器,所以没有"发送回发送者"功能。您唯一的选择是不断将消息转发到下一步。
因此,我认为您只需要在消息周围添加一些额外的结构。 Either
构造似乎对此有用。让我们假设您的CreateUser
Actor具有自包含功能:
def createUserFunction(createUser : CreateUser) : UserCreated = ???
然后可以跟一个函数CheckEmail
:
val Set[String] existingEmails = ???
def checkEmailUniqueness(userCreated : UserCreated) : Either[CreateUserError, UserCreated] =
if(existingEmails contains userCreated.email)
Left(EmailIsAlreadyInUse(userCreated.email))
else
Right(createUser)
同样地,3rdPartyCall
woul也会返回一个Either:
def thirdPartyLibraryFunction(userCreated : UserCreated) : Boolean = ???
def thirdPartyCall(userCreated : UserCreated) : Either[CreateUserError, UserCreated] =
if(!thirdPartyLibraryFunction(userCreated))
Left(NoUserInExternalDB(userCreated.email))
else
Right(userCreated)
Akka Stream Construction
使用此结构化消息传递,您现在可以创建仅在单个方向上移动的流。我们首先制作一个用于创建用户的Flow
:
val createUserFlow : Flow[CreateUser, UserCreated, _] =
Flow[CreateUser] map (createUserFunction)
然后通过电子邮件检查流程:
val emailFlow : Flow[UserCreated, Either[CreateUserError, UserCreated],_] =
Flow[UserCreated] map (checkEmailUniqueness)
现在让Flow进行第三方通话:
val thirdPartyFlow : Flow[UserCreated, Either[CreateUserError, UserCreated],_] =
Flow[UserCreated] map (_ flatMap thirdPartyCall)
这些流现在可以构成流的基础以及Source
和Sink
:
val userSource : Source[CreateUser, _] = ???
val userSink : Sink[Either[CreateUserError, UserCreated], _] =
Sink[Either[CreateUserError, UserCreated]] foreach {
case Left(error) =>
System.err.println("Error with user creation : " error.email)
case Right(userCreated) =>
System.out.println("User Created: " userCreated.email)
}
//create the full stream
userSource
.via(createUserFlow)
.via(emailFlow)
.via(thirdPartyFlow)
.to(userSink)
.run()