Orleans 2.0 官方文档 —— 4.10.6 Grains -> 高级功能 -> Orleans 事务

Orleans 事务

对于持久化的grain状态,Orleans支持分布式ACID事务。

设置

Orleans事务是选择性的。必须将silo配置为使用事务。如果不是,任何对grain事务性的方法的调用,都会收到OrleansTransactionsDisabledException。要在silo上启用事务,请在silo host builder上,调用UseTransactions()

var builder = new SiloHostBuilder().UseTransactions();

交易型的状态存储

要使用事务,用户需要配置数据存储。为了支持具有事务的各种数据存储,引入了存储抽象ITransactionalStateStorage。与泛型的grain存储(IGrainStorage)不同,这种抽象特定于交易需求。要使用特定于事务的存储,用户可以使用ITransactionalStateStorage的任何实现,例如Azure(AddAzureTableTransactionalStateStorage),来配置silo。

例:


var builder = new SiloHostBuilder()
    .AddAzureTableTransactionalStateStorage("TransactionStore", options =>
    {
        options.ConnectionString = ”YOUR_STORAGE_CONNECTION_STRING”);
    })
    .UseTransactions();

出于开发目的,如果特定于事务的存储不适用于您需要的数据存储,则可以使用IGrainStorage实现。对于没有为其配置存储的任何事务性的状态,事务将尝试使用桥接器,把故障转移到grain存储。通过桥接到grain存储,访问事务性的状态的效率较低,并不是我们打算长期支持的模式,因此建议仅将其用于开发目的。

编程模型

grain interfaces

为了让grain支持事务,必须使用“Transaction”属性,将grain接口上的事务性的方法,标记为事务的一部分。该属性需要通过以下事务选项,指示grain调用在事务环境中的行为:

  • TransactionOption.Create - 调用是事务性的,并且将始终创建新的事务上下文(即,它将启动一个新的事务),即使在现有事务上下文中调用也是如此。
  • TransactionOption.Join - 调用是事务性的,但只能在现有事务的上下文中调用。
  • TransactionOption.CreateOrJoin - 调用是事务性的。如果在事务的上下文中调用,它将使用该上下文,否则它将创建新的上下文。
  • TransactionOption.Suppress - 调用不是事务性的,但可以从事务内部调用。如果在事务的上下文中调用,则不会将上下文传递给调用。
  • TransactionOption.Supported - 调用不是事务性的,但支持事务。如果在事务的上下文中调用,则上下文将传递给调用。
  • TransactionOption.NotAllowed - 调用不是事务性的,不能从事务中调用。如果在事务的上下文中调用,它将抛出一个NotSupportedException

呼叫可以标记为“Create”,这意味着调用将始终启动自己的事务。例如,下面的ATM grain中的转账操作,将始终启动一个涉及两个引用帐户的新事务。


public interface IATMGrain : IGrainWithIntegerKey
{
    [Transaction(TransactionOption.Create)]
    Task Transfer(Guid fromAccount, Guid toAccount, uint amountToTransfer);
}

账户grain上的事务性操作Withdraw和Deposit标记为“Join”,表示只能在现有事务的上下文中调用它们,如果在IATMGrain.Transfer(…)期间调用它们,就是这种情况。GetBalance调用被标记为CreateOrJoin,因此可以从现有事务中调用它,例如通过IATMGrain.Transfer(…),或单独调用。


public interface IAccountGrain : IGrainWithGuidKey
{
    [Transaction(TransactionOption.Join)]
    Task Withdraw(uint amount);

    [Transaction(TransactionOption.Join)]
    Task Deposit(uint amount);

    [Transaction(TransactionOption.CreateOrJoin)]
    Task<uint> GetBalance();
}

grain的实现

grain的实现需要使用ITransactionalState Facet(参考Facet系统),通过ACID事务,来管理grain的状态。


    public interface ITransactionalState<TState>  
        where TState : class, new()
    {
        Task PerformRead(Func readFunction);
        Task PerformUpdate(Func updateFunction);
    }

必须通过传递给事务性状态facet的同步函数,来执行对持久化状态的所有读或写访问。这允许事务系统以事务方式执行或取消这些操作。要在grain中使用事务性状态,只需要定义要持久化的可序列化的状态类,并在grain的构造函数中,使用TransactionalState属性声明事务性状态。后者声明状态名称和(可选地)要使用哪个事务性状态存储(请参阅设置)。


[AttributeUsage(AttributeTargets.Parameter)]
public class TransactionalStateAttribute : Attribute
{
    public TransactionalStateAttribute(string stateName, string storageName = null)
    {
      …
    }
}

例:


public class AccountGrain : Grain, IAccountGrain
{
    private readonly ITransactionalState balance;

    public AccountGrain(
        [TransactionalState("balance", "TransactionStore")]
        ITransactionalState balance)
    {
        this.balance = balance ?? throw new ArgumentNullException(nameof(balance));
    }

    Task IAccountGrain.Deposit(uint amount)
    {
        return this.balance.PerformUpdate(x => x.Value += amount);
    }

    Task IAccountGrain.Withdrawal(uint amount)
    {
        return this.balance.PerformUpdate(x => x.Value -= amount);
    }

    Task<uint> IAccountGrain.GetBalance()
    {
        return this.balance.PerformRead(x => x.Value);
    }
}

在上面的示例中,属性TransactionalState用于声明'balance'构造函数参数应与名为“balance”的事务性状态相关联。通过此声明,Orleans将注入一个ITransactionalState实例,该实例具有从名为“TransactionStore”的事务性状态存储加载的状态(请参阅设置)。状态可以通过PerformUpdate修改,也可以通过PerformRead读取。事务基础设施将确保作为事务的一部分执行的任何此类更改,即使在分布在Orleans集群上的多个grain中,在完成创建事务的grain(上述示例中的IATMGrain.Transfer)调用完成后,将会要么全部提交,要么全部撤消。

调用事务

grain接口上的事务性方法的调用,与任何其他的grain调用一样。


    IATMGrain atm = client.GetGrain(0);
    Guid from = Guid.NewGuid();
    Guid to = Guid.NewGuid();
    await atm.Transfer(from, to, 100);
    uint fromBalance = await client.GetGrain(from).GetBalance();
    uint toBalance = await client.GetGrain(to).GetBalance();

在上述调用中,ATM grain用于将100个单位的货币从一个帐户转账到另一个帐户。转账完成后,查询两个账户的当前余额。货币转账以及两个帐户的查询都作为ACID事务执行。

如上例所示,事务可以像其他grain调用一样,返回Task中的值,但是在调用失败时,它们不会抛出应用程序异常,而是抛出一个OrleansTransactionExceptionTimeoutException。如果应用程序在事务期间抛出异常,并且该异常导致事务失败(而不是由于其他系统故障导致失败),则应用程序异常将是OrleansTransactionException的内部异常。如果抛出类型为OrleansTransactionAbortedException的事务异常,则该事务失败,但可以重试。抛出的任何其他异常,都表示事务以未知状态终止。由于事务是分布式操作,因此处于未知状态的事务可能已成功、失败或仍在进行中。因此,建议在验证状态或重试操作之前,允许度过一个调用的超时周期(SiloMessagingOptions.ResponseTimeout),以避免级联中止。

你可能感兴趣的:(Orleans)