WorkflowCore中默认的QueueProvider - SingleNodeQueueProvider

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");
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值