这个问题困扰我有一段时间了(希望我不是唯一一个)。我想找一个典型的3层Java EE应用程序,并看看它如何可能使用actors实现。我想知道是否真的有意义进行这样的转换,如果有意义,我如何从中受益(例如性能,更好的架构,可扩展性,可维护性等)。
以下是典型的Controller(表示层),Service(业务逻辑),DAO(数据):
trait UserDao {
def getUsers(): List[User]
def getUser(id: Int): User
def addUser(user: User)
}
trait UserService {
def getUsers(): List[User]
def getUser(id: Int): User
def addUser(user: User): Unit
@Transactional
def makeSomethingWithUsers(): Unit
}
@Controller
class UserController {
@Get
def getUsers(): NodeSeq = ...
@Get
def getUser(id: Int): NodeSeq = ...
@Post
def addUser(user: User): Unit = { ... }
}
你可以在许多Spring应用程序中找到类似的内容。我们可以采取简单的实现方式,它没有任何共享状态,因此不需要同步块......因此所有状态都在数据库中,并且应用程序依赖于事务。Service、Controller和Dao仅有一个实例。因此对于每个请求,应用服务器将使用单独的线程,但是线程不会相互阻塞(但会被DB IO阻塞)。假设我们正在尝试使用Actor实现类似的功能。它可能是这样的:
sealed trait UserActions
case class GetUsers extends UserActions
case class GetUser(id: Int) extends UserActions
case class AddUser(user: User) extends UserActions
case class MakeSomethingWithUsers extends UserActions
val dao = actor {
case GetUsers() => ...
case GetUser(userId) => ...
case AddUser(user) => ...
}
val service = actor {
case GetUsers() => ...
case GetUser(userId) => ...
case AddUser(user) => ...
case MakeSomethingWithUsers() => ...
}
val controller = actor {
case Get("/users") => ...
case Get("/user", userId) => ...
case Post("/add-user", user) => ...
}
我认为在这里不太重要的是Get()和Post()提取器的实现方式。假设我编写了一个框架来实现这一点,我可以像这样向控制器发送消息:
controller !! Get("/users")
控制器和服务将执行相同的事情。在这种情况下,整个工作流程将是同步的。更糟糕的是 - 我一次只能处理一个请求(同时所有其他请求都会落入控制器的邮箱中)。因此我需要将它全部变成异步。在这种设置中,有没有优雅的方法可以异步执行每个处理步骤?
据我所了解,每个层应该以某种方式保存其接收到的消息的上下文,然后向下一层发送消息。当下一层回复某个结果消息时,我应该能够恢复初始上下文并将此结果回复给原始发送方。这正确吗?
此外,目前对于每个层,我只有一个Actor实例。即使它们异步工作,我仍然只能并行处理一个控制器、服务和dao消息。这意味着我需要更多相同类型的Actor。这导致每个层都需要LoadBalancer。这也意味着,如果我有UserService和ItemService,我应该分别负载均衡它们。
我有一种感觉,我可能理解错了什么。所有需要的配置似乎过于复杂。你对此有什么看法?
(PS:了解DB事务如何适合这种情况也很有趣,但我认为这对于这个主题来说太过复杂了。)