我正在尝试创建并行事件订阅者。这是我的第一次尝试:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EventStore.ClientAPI;
namespace Sandbox
{
public class SomeEventSubscriber
{
private Position? _latestPosition;
private readonly Dictionary<Type, Action<object>> _eventHandlerMapping;
private IEventStoreConnection _connection;
public Dictionary<Type, Action<object>> EventHandlerMapping
{
get { return _eventHandlerMapping; }
}
public SomeEventSubscriber()
{
_eventHandlerMapping = CreateEventHandlerMapping();
_latestPosition = Position.Start;
}
public void Start()
{
ConnectToEventstore();
}
private void ConnectToEventstore()
{
_connection = EventStoreConnectionWrapper.Connect();
_connection.Connected +=
(sender, args) => _connection.SubscribeToAllFrom(_latestPosition, false, EventOccured, LiveProcessingStarted, HandleSubscriptionDropped);
}
private Dictionary<Type, Action<object>> CreateEventHandlerMapping()
{
return new Dictionary<Type, Action<object>>
{
{typeof (FakeEvent1), o => Handle(o as FakeEvent1)},
{typeof (FakeEvent2), o => Handle(o as FakeEvent2)},
};
}
private async Task Handle(FakeEvent1 eventToHandle)
{
SomethingLongRunning(eventToHandle);
}
private async Task Handle(FakeEvent2 eventToHandle)
{
SomethingLongRunning(eventToHandle);
}
private async Task SomethingLongRunning(BaseFakeEvent eventToHandle)
{
Console.WriteLine("Start Handling: " + eventToHandle.GetType());
var task = Task.Delay(10000);
await task;
Console.WriteLine("Finished Handling: " + eventToHandle.GetType());
}
private void EventOccured(EventStoreCatchUpSubscription eventStoreCatchUpSubscription,
ResolvedEvent resolvedEvent)
{
if (resolvedEvent.OriginalEvent.EventType.StartsWith("$") || resolvedEvent.OriginalEvent.EventStreamId.StartsWith("$"))
return;
var @event = EventSerialization.DeserializeEvent(resolvedEvent.OriginalEvent);
if (@event != null)
{
var eventType = @event.GetType();
if (_eventHandlerMapping.ContainsKey(eventType))
{
var task = Task.Factory.StartNew(() => _eventHandlerMapping[eventType](event));
Console.WriteLine("The task is running asynchronously...");
}
}
if (resolvedEvent.OriginalPosition != null) _latestPosition = resolvedEvent.OriginalPosition.Value;
}
private void HandleSubscriptionDropped(EventStoreCatchUpSubscription subscription, SubscriptionDropReason dropReason, Exception ex)
{
if (dropReason == SubscriptionDropReason.ProcessingQueueOverflow)
{
//TODO: Wait and reconnect probably with back off
}
if (dropReason == SubscriptionDropReason.UserInitiated)
return;
if (SubscriptionDropMayBeRecoverable(dropReason))
{
Start();
}
}
private static bool SubscriptionDropMayBeRecoverable(SubscriptionDropReason dropReason)
{
return dropReason == SubscriptionDropReason.Unknown || dropReason == SubscriptionDropReason.SubscribingError ||
dropReason == SubscriptionDropReason.ServerError || dropReason == SubscriptionDropReason.ConnectionClosed;
}
private static void LiveProcessingStarted(EventStoreCatchUpSubscription eventStoreCatchUpSubscription)
{
}
}
}
在您的专家意见中,这是一种有效的方法吗?你能否提出任何改进建议?
PS:
也许:
Task.Run(() => _eventHandlerMapping[eventType](@event));
会更好吗?
答案 0 :(得分:1)
您只有一个EventOccured
代表,您可以在EventStore
发生的所有事件中收到通知
首先考虑在EventOccured
内的预编码中运行与事件被触发的调度程序不同的调度程序
其次,您是否可以将此更改为abstract class
并实现FakeEventBase
,然后对其进行扩展并为每个FakeEvent
类型创建单个实例。
这将是更清洁的解决方案
第三,考虑使用自定义ThreadScheduler
来排队和运行这些Handle
任务。
http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler(v=vs.110).aspx
编辑:
我会有一个类似下面的广播组,它知道操作何时完成并提升已完成的事件。
public class EventBroadcaster
{
public event EventHandler SomeEventOccured;
public async void DoLongRunningOperationAndRaiseFinishedEvent()
{
var waitingTask = Task.Delay(TimeSpan.FromSeconds(2));
await waitingTask.ContinueWith(t => RaiseSomeEventOccured(), CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current);
}
private void RaiseSomeEventOccured()
{
EventHandler handler = SomeEventOccured;
if (handler != null) handler(this, EventArgs.Empty);
}
}
然后是一个EventListener
public class EventListner
{
private readonly string _id;
public EventListner(string id)
{
_id = id;
}
public void ListenTo(EventBroadcaster broadcaster)
{
broadcaster.SomeEventOccured += OnSomeEventOccured;
}
private async void OnSomeEventOccured(object sender, EventArgs eventArgs)
{
var currentTime = DateTime.Now;
Console.WriteLine("EventListner {0} received at {1}", _id,
currentTime.ToString("dd-MM-yyyy HH:mm:ss.fffffff"));
//Not required just to show this does not affect other instances.
//await Task.Delay(TimeSpan.FromSeconds(5));
}
}
然后这将是用于测试的Program.cs
public static class Program
{
public static void Main(string[] args)
{
var broadcaster = new EventBroadcaster();
var listners = new List<EventListner>();
for (int i = 1; i < 10; i++)
{
var listner = new EventListner(i.ToString(CultureInfo.InvariantCulture));
listner.ListenTo(broadcaster);
listners.Add(listner);
}
broadcaster.DoLongRunningOperationAndRaiseFinishedEvent();
Console.WriteLine("Waiting for operation to complete");
Console.ReadLine();
}
}
在此示例中,处理程序委托按订阅顺序逐个触发。
现在将Broadcaster中的代码修改为如下所示
注意:我已将方法签名从EventHandler
更改为Action
,以便于编码。
private void RaiseSomeEventOccured()
{
Action handler = SomeEventOccured;
if (handler != null)
{
var parallelOption = new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount };
Parallel.Invoke(parallelOption, Array.ConvertAll(handler.GetInvocationList(), ConvertToAction));
handler();
}
}
private Action ConvertToAction(Delegate del)
{
return (Action)del;
}
现在您将看到事件以随机顺序触发
我使用选项1获得了更好的表现
注意:始终使用TPL
和Parallel
编程,您需要确保在使用之前有一个好处。
答案 1 :(得分:1)
我真的没有看到创建并行事件订阅者的一点(如果我理解你的意图是正确的 - 能够并行运行事件处理程序,而不是像正常那样一个接一个地运行事件)。
如果事件处理程序本身显示,则表示意图并行运行 clear 。
像(非常原始)的东西。
void SomeEventHandler(object sender, EventArgs e)
{
Task.Run(() =>
{
... // some code to run in parallel
});
}
你可能想要创建一种经理(老实说,我不知道如何占用所有核心,但我不认为这很复杂,我从来没有必要这样做),但请保持正常事件