WorkflowCore中默认的QueueProvider - SingleNodeQueueProvider
- 默认为 SingleNodeQueueProvider
- 其它扩展有:
- AWS: SQSQueueProvider
- Azure: AzureStorageQueueProvider
- Redis: RedisQueueProvider
- RabbitMQ: RabbitMQProvider
- SqlServer: SqlServerQueueProvider
接口 (IQueueProvider)
# 入队列
Task QueueWork(string id, QueueType queue);
# 出队列,如果队列为空则返回null
Task<string> DequeueWork(QueueType queue, CancellationToken cancellationToken);
# 出列时,是否阻塞
bool IsDequeueBlocking { get; }
# 队列开始工作(如果有)
Task Start();
# 队列停止工作(如果有)
Task Stop();
SingleNodeQueueProvider
public class SingleNodeQueueProvider : IQueueProvider
{
private readonly Dictionary<QueueType, BlockingCollection<string>> _queues = new Dictionary<QueueType, BlockingCollection<string>>()
{
# 每个型类创建一个自带阻塞功能的线程安全集合
[QueueType.Workflow] = new BlockingCollection<string>(),
[QueueType.Event] = new BlockingCollection<string>(),
[QueueType.Index] = new BlockingCollection<string>()
};
public bool IsDequeueBlocking => true;
# 入列
public Task QueueWork(string id, QueueType queue)
{
_queues[queue].Add(id);
return Task.CompletedTask;
}
# 出列
public Task<string> DequeueWork(QueueType queue, CancellationToken cancellationToken)
{
if (_queues[queue].TryTake(out string id, 100, cancellationToken))
return Task.FromResult(id);
return Task.FromResult<string>(null);
}
// ...
}
QueueConsumer 消费者队列
internal abstract class QueueConsumer : IBackgroundTask
{
protected abstract QueueType Queue { get; }
protected virtual int MaxConcurrentItems => Math.Max(Environment.ProcessorCount, 2);
# 是否允许第二遍执行
protected virtual bool EnableSecondPasses => false;
protected readonly IQueueProvider QueueProvider;
// ...
protected Task DispatchTask;
private CancellationTokenSource _cancellationTokenSource;
protected QueueConsumer(IQueueProvider queueProvider,...)
{
QueueProvider = queueProvider;
// ...
}
protected abstract Task ProcessItem(string itemId, CancellationToken cancellationToken);
public virtual void Start()
{
if (DispatchTask != null)
{
throw new InvalidOperationException();
}
_cancellationTokenSource = new CancellationTokenSource();
DispatchTask = new Task(Execute, TaskCreationOptions.LongRunning);
DispatchTask.Start();
}
public virtual void Stop()
{
_cancellationTokenSource.Cancel();
DispatchTask.Wait();
DispatchTask = null;
}
private async void Execute()
{
var cancelToken = _cancellationTokenSource.Token;
# 将要或者正在执行的任务
var activeTasks = new Dictionary<string, Task>();
# 查重集合
var secondPasses = new HashSet<string>();
while (!cancelToken.IsCancellationRequested)
{
try
{
# 限流
if (activeTasks.Count >= MaxConcurrentItems)
{
await Task.Delay(Options.IdleTime);
continue;
}
# 出列
var item = await QueueProvider.DequeueWork(Queue, cancelToken);
# 出列为null,说明队列为空
if (item == null)
{
# 针对非阻塞的队列增加延时
if (!QueueProvider.IsDequeueBlocking)
await Task.Delay(Options.IdleTime, cancelToken);
continue;
}
# 出列成功,判断是否在活动的任务中
if (activeTasks.ContainsKey(item))
{
// # Line1
# 加入查重集合
secondPasses.Add(item);
# 未开启第二遍执行,就把拿出来的item放回原来的队列
if (!EnableSecondPasses)
await QueueProvider.QueueWork(item, Queue);
continue;
}
# 从查重集合中移除
secondPasses.Remove(item);
var task = new Task(async (object data) =>
{
try
{
# 第一次执行
await ExecuteItem((string)data);
// # Line2
# 开启第二遍执行时,根据查重集合判断是否继续执行
while (EnableSecondPasses && secondPasses.Contains(item))
{
secondPasses.Remove(item);
await ExecuteItem((string)data);
}
}
finally
{
# 从活动集合中移除
lock (activeTasks)
{
activeTasks.Remove((string)data);
}
}
}, item);
# 加入活动集合
lock (activeTasks)
{
activeTasks.Add(item, task);
}
// # Line 3
# 开始执行Task
task.Start();
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
}
}
# 等待所有活动的任务执行完
await Task.WhenAll(activeTasks.Values);
}
Line3 -> (Line1) -> Line2 ,从Line3执行到Line2期间存在同一个item执行到Line1
WorkflowCore的后台任务
- WorkflowConsumer 工作流消费者(处理QueueType.Workflow类型的任务)
- EventConsumer 事件消费者 (处理QueueType.Event类型的任务)
- IndexConsumer 搜索消费者 (处理QueueType.Index类型的任务)
- RunnablePoller 轮询类 (把可执行的Workflow和Event放入队列中)
默认每10秒钟轮询一次
WorkflowConsumer、EventConsumer和IndexConsumer全部共同消费同一个 QueueProvider。RunnablePoller是QueueProvider的生产者之一。
QueueType的生产者
WorkflowController.StartWorkflow 生产 QueueType.Workflow 和 QueueType.Index
public async Task<string> StartWorkflow<TData>(string workflowId, int? version, TData data = null, string reference=null)
where TData : class, new()
{
// ...
string id = await _persistenceStore.CreateNewWorkflow(wf);
await _queueProvider.QueueWork(id, QueueType.Workflow);
await _queueProvider.QueueWork(id, QueueType.Index);
await _eventHub.PublishNotification(...);
return id;
}
WorkflowController.PublishEvent 生产 QueueType.Event
public async Task PublishEvent(string eventName, string eventKey, object eventData, DateTime? effectiveDate = null)
{
// ...
string eventId = await _persistenceStore.CreateEvent(evt);
await _queueProvider.QueueWork(eventId, QueueType.Event);
}
WorkflowController.SuspendWorkflow 、ResumeWorkflow和TerminateWorkflow 生产 QueueType.Index
public async Task<bool> SuspendWorkflow(string workflowId)
{
// ...
if (wf.Status == WorkflowStatus.Runnable)
{
wf.Status = WorkflowStatus.Suspended;
await _persistenceStore.PersistWorkflow(wf);
await _queueProvider.QueueWork(workflowId, QueueType.Index);
await _eventHub.PublishNotification(...);
return true;
}
// ...
}
public async Task<bool> ResumeWorkflow(string workflowId)
{
// ...
bool requeue = false;
try
{
var wf = await _persistenceStore.GetWorkflowInstance(workflowId);
if (wf.Status == WorkflowStatus.Suspended)
{
wf.Status = WorkflowStatus.Runnable;
await _persistenceStore.PersistWorkflow(wf);
requeue = true;
await _queueProvider.QueueWork(workflowId, QueueType.Index);
await _eventHub.PublishNotification(...);
return true;
}
return false;
}
finally
{
await _lockProvider.ReleaseLock(workflowId);
# 重新入列
if (requeue)
await _queueProvider.QueueWork(workflowId, QueueType.Workflow);
}
// ...
}
public async Task<bool> ResumeWorkflow(string workflowId)
{
// ...
var wf = await _persistenceStore.GetWorkflowInstance(workflowId);
wf.Status = WorkflowStatus.Terminated;
await _persistenceStore.PersistWorkflow(wf);
await _queueProvider.QueueWork(workflowId, QueueType.Index);
await _eventHub.PublishNotification(...);
return true;
// ...
}
关于QueueType.Index 类型
默认情况下 QueueType.Index 类型的Task为空方法。
使用Elasticsearch作用WorkflowCore的检索插件
WorkflowCore.Providers.Elasticsearch
services.AddWorkflow(cfg =>
{
...
cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://localhost:9200")), "index_name");
});