奥尔良(Orleans),Akka.net,理解演员模型的问题

3
如果你不懂C#但熟悉演员模型,请阅读下面的问题,因为它更多关于架构和数据管理方面的内容。
我是一名非常初级的C#开发者,正在尝试理解演员模型。我已经有了一定的了解,但还有一个问题让我无法理解。
在我提出问题之前,让我描述一下背景,以便更好地理解。
作为一个测试示例,我想为一个虚构的银行构建一个应用程序。我打算使用akka.net和Orleans来实现这个应用程序,以便学习并比较它们。
使用情况:
- 作为用户,我希望能够创建一个新帐户; - 作为用户,我希望能够使用我的帐户唯一编号登录应用程序; - 作为用户,我希望能够向我的帐户存入资金; - 作为用户,我希望能够选择另一个用户并将指定金额的资金转移到他们的帐户中; - 作为用户,我希望能够从我的帐户中提取一笔资金。
因此,存在以下实体:
- 用户; - 帐户。
识别用户和其帐户之间的一对一关系。我将使用ORM将这些数据存储在我的数据库中。显然,模型看起来像这样:
public class User
{
    public Guid Id { get; set; }
    public string FullName { get; set; }
    ....
}

public class Account
{
    public Guid Id { get; set; }
    public string UniqueNumber { get; set; }
    public string Balance { get; set; }
    ...
}

我也希望拥有两个角色/领域专家:

  1. 账户角色(AccountActor);
  2. 交易服务(TransactionService)。

它们的接口:

//Or IAccountGrain
public interface IAccountActor
{
    void Deposit(Money amount);
    void Withdraw(Money amount);
}

//Or ITransactionGrain
public interface ITransactionActor
{
    void Transfer(IAccountActor from, IAccountActor to, Money amount);
}

我不理解的是如何处理关系型数据库中的数据。 让我们想象以下情景: 50个用户在线并通过客户端应用程序积极向应用程序的REST API发出请求。他们几乎没有停顿地提取、存款和转账。
问题是: 1.我是否应该为每个用户帐户创建一个actor?我非常确定需要这样做,因为如何才能实现不同账户之间的数千次交易。 2.我如何将用户帐户与AccountActor关联起来?如果我在actor的激活/启动之前使用repository从数据库加载数据并设置状态,这样做是否正确?
主要问题是:如何将状态保存回数据库? 假设账户A有1000美元,用户发起了大约100笔涉及该账户的交易。账户A的状态在消息之间发生变化。 什么是将这些更改保存到数据库的最佳方法?我读到如果我直接从actor调用数据库,我会失去所有好处,因为操作会被阻塞。
我是否应该创建一个额外的actor来处理其他actor发送的消息,并使用repository将更改写入数据库? 我的意思是,我可以从AccountActor发送关于帐户更改的消息到新actor,在那里我将调用适当的repository。但是,这不是瓶颈吗?让我们想象在线1000个用户,有大约10万个账户之间的交易。那么负责将帐户更改保存到数据库的actor可能有太多消息要处理。
对于这篇长文,我感到抱歉。我试图找到使用Orleans或Akka.net的应用程序示例,但我没有找到任何利用数据库的东西。 谢谢您的关注。

将您的思维从演员和数据库中解耦。假装您只有一个持久性演员(也许它是一个存储关系型数据库,也许它是一个非关系型数据库...或者更奇特的东西)。 - BozoJoe
@BozoJoe 你的意思是如果应用程序的所有状态都由保存在数据库中的actor表示(我不是指每个业务实体,而是像akka.persistence一样)?但是,如果我想要一些关系型存储,那么我只需要找到一种同步“应用程序状态”(持久化actor)和我的关系型存储的方法吗?这正确吗? - Ivan avn
@BozoJoe 是否正确地认为状态是由应用程序本身(Actor)维护的?如果需要,我们可以将状态与其他类型的存储同步(可能使用事件源)。 - Ivan avn
你开始理解了。 - BozoJoe
@BozoJoe 非常感谢。 - Ivan avn
1个回答

4

这里有几个你可能遗漏了的概念,但让我们按顺序回答问题。

我应该为每个用户帐户创建一个演员吗?我很确定需要这样做,因为如果不这样做,我怎么能实现不同帐户之间的数千个交易呢?

我猜你在考虑的替代方案是每个用户账户多个演员,但这是错误的。每个用户账户只能有一个演员,否则你会遇到你描述的问题:同时发出请求可能会导致同一笔资金被提取两次。

我如何将用户帐户与AccountActor关联起来?

你缺少一个UserActor,它拥有AccountActors。一个账户没有所有人是不存在的,否则我们不知道账户中的钱是谁的。在现实世界中,人们通常不会把钱汇到随机的账户上,他们想要把钱汇给一个人,并使用发送者的用户角色账户进行转账。

如果在演员启动/开始之前从数据库使用存储库加载数据并设置状态是否正确?

是的,事实上这是强制性的。如果演员没有状态,那么它就没什么用处。

保存这些更改到数据库的最佳方法是什么?我看到如果我直接从演员调用数据库,我将失去所有优势,因为操作会被阻塞。我应该再创建一个演员来处理其他演员的消息,并使用存储库将更改写入数据库吗?

你走在正确的道路上,但还没到那里。保存演员状态需要使用异步方法进行数据库写入。使用异步方法时,主线程不会阻塞等待数据库写入,因此处理线程可以继续其业务。

当只有一个演员参与动作时,它可以通过异步方法保存自己的状态。在银行登记簿中,总是涉及两个账户,必须要保证两个账户写入成功或失败,永远不能一个成功一个失败。因此,TransactionActor将打开一个DB事务,并告诉每个AccountActor使用该DB事务保存其状态。如果任何一个失败,则中止事务并导致两者都失败。请注意,此方法是TransactionActor的私有异步方法,因此您可以获得并行处理的优势。

顺便说一下,在Orleans中找不到编写到数据库的示例,因为这些都由框架自动处理。保存方法自动是异步的,并与数据库交互。在Orleans中,您只需要引用演员,状态就会从数据库中自动拉取。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接