Loading

Learn Orleans 06 - 事件溯源(Event Sourcing)

https://github.com/wswind/learn-orleans/tree/master/04.EventSourcing

上一讲我们说明了多个grain的运行,各有各的状态,事件溯源机制能够帮助我们将多个grain状态同步。这在大型分布式系统中,意味着横向拓展,消除系统的存储瓶颈。
下图源自:https://www.cnblogs.com/sheng-jie/p/11163264.html

事件溯源一般与CQRS的备忘录模式配合使用。Actor不断接收并响应事件,来修改当前的状态。
下图源自:https://www.cnblogs.com/gaopang/p/8457990.html

orleans提供了两个例子来讲解事件溯源
https://github.com/dotnet/orleans/tree/master/Samples/1.x/ReplicatedChatGrainSample
https://github.com/dotnet/orleans/tree/master/Samples/1.x/ReplicatedEventSample

Orleans提供了三个Log-Consistency Providers来进行一致性存储,它们之间的区别及注意实现请见:
https://dotnet.github.io/orleans/Documentation/grains/event_sourcing/log_consistency_providers.html

本文借助用于演示的Orleans.EventSourcing.LogStorage.LogConsistencyProvider来演示
各项目的依赖包安装说明:https://dotnet.github.io/orleans/Documentation/grains/event_sourcing/event_sourcing_configuration.html

Silo添加LogConsistencyProvider,它通过内存来存储事件,并处理多grain的一致性

builder.AddMemoryGrainStorageAsDefault().AddLogStorageBasedLogConsistencyProvider("LogStorage")

Interface中,添加了一个新的事件接收接口

Task NewEvent(EventData @event);

EventData为所有事件的父类。我定义了两个事件,一个数量增加,一个数量减少

namespace OrleansBasics
{
    [Serializable]
    public class EventData
    {
        public EventData()
        {
            When = DateTime.UtcNow;
        }

        public DateTime When;
    }

    public class EventDataAdd : EventData
    {
        public int AddCount;
    }

    public class EventDataMinus : EventData
    {
        public int MinusCount;
    }
}

Grain需要继承JournaledGrain,通过模板参数,指定状态类,以及事件类。
HelloGrain通过LogConsistencyProvider标签指定Provider
接口的实现,就是触发事件,然后提交事件。

[LogConsistencyProvider(ProviderName = "LogStorage")]
public class HelloGrain : JournaledGrain<HelloState, EventData>, IHello
{
  public async Task NewEvent(EventData @event)
  {
      RaiseEvent(@event);
      await ConfirmEvents();
  }

}

处理事件的状态改变代码逻辑,写在HelloState状态类中

namespace OrleansBasics
{
    public class HelloState
    {
        public int Count { get; set; }
        public void Apply(EventDataAdd addData)
        {
            Count += addData.AddCount;
        }
        public void Apply(EventDataMinus minusData)
        {
            Count -= minusData.MinusCount;
        }
    }
}

除了Apply模式外,还可以通过复写TransitionState方法来处理逻辑

protected override void TransitionState(State state, EventType @event)

client调用时,创建事件,并调用client

var client1 = client.GetGrain<IHello>(0);
var dataAdd = new EventDataAdd
{
    AddCount = 3
};
await client1.NewEvent(dataAdd);
var count = await client1.GetCount();
Console.WriteLine(count);

对于频繁访问的grain应该使用延迟提交机制
https://dotnet.github.io/orleans/Documentation/grains/event_sourcing/immediate_vs_delayed_confirmation.html
对于多grain的同步,可参考:
https://dotnet.github.io/orleans/Documentation/grains/event_sourcing/replicated_instances.html
由于多grain的事件可能发生冲突,而grain又不会有同步锁机制,因此会通过RaiseConditionalEvent来进行event校验,取消执行发生冲突的事件。
多grain的状态同步,可以通过RefreshNow方法来执行。该方法会提交所有的事件,并从storage中加载最新版本。

也就是说,orleans的事件溯源中,grain提供了事件监听,并基于事件变更状态的机制。事件会存储与storage中。各storage会同步事件,来更新到最新状态。而事件的冲突问题,需要另外通过RaiseConditionalEvent来处理。

这个例子只是帮助我们理解事件溯源的机制,真实环境的使用,需要自行实现CustomStorage。具体实现可参考:https://github.com/dotnet/orleans/tree/master/Samples/1.x/ReplicatedEventSample

posted @ 2020-04-01 17:27  wswind  阅读(847)  评论(0编辑  收藏  举报