Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/156.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ionic-framework/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用Polly阻止发布到TPL管道的大量作业_C#_Task Parallel Library_Tpl Dataflow_Polly - Fatal编程技术网

C# 如何使用Polly阻止发布到TPL管道的大量作业

C# 如何使用Polly阻止发布到TPL管道的大量作业,c#,task-parallel-library,tpl-dataflow,polly,C#,Task Parallel Library,Tpl Dataflow,Polly,我正在探索如何实现TPL数据流管道的不同方法。请按照代码示例和所有注释理解我的问题。以下是两个简单的模块: //DataClass is very simple class with two properties int Id, and enum Status var downloadBlock = new TransformBlock<DataClass, DataClass>((data) => { //Here I use Polly library to hel

我正在探索如何实现
TPL数据流
管道的不同方法。请按照代码示例和所有注释理解我的问题。以下是两个简单的模块:

//DataClass is very simple class with two properties int Id, and enum Status
var downloadBlock = new TransformBlock<DataClass, DataClass>((data) =>
{
    //Here I use Polly library to help with retrying when exception has occured
    //I have chosen to retry only on WebException, because in this case there is 
    //no need to try ObjectNotFoundException
    var policy = Policy.Handle<WebException>().WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(1));
    try
    {
        policy.Execute(() =>
        {
            //ThisMethodMyThrowWebException();
            data.Status = Status.Completed;
        });
    }
    catch (ObjectNotFoundException)
    {
        data.Status = Status.Failed;
        //Notify user that object is not found;
    }
    catch (WebException we)
    {
        data.Status = Status.Failed;
        //I would like to cancel whole batch of jobs that were sent to pipeline.
    }
    return data;
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1 });


var actionBlock = new ActionBlock<DataClass>((data) =>
{
    if (data.Status != Status.Failed)
    {
        //Do something with data
    }
});

downloadBlock.LinkTo(actionBlock);
//DataClass是一个非常简单的类,有两个属性int Id和enum Status
var downloadBlock=新TransformBlock((数据)=>
{
//在这里,我使用Polly库来帮助在发生异常时重试
//我选择只在WebException上重试,因为在这种情况下
//无需尝试ObjectNotFoundException
var policy=policy.Handle();
尝试
{
policy.Execute(()=>
{
//ThisMethodMyRowWebException();
data.Status=Status.Completed;
});
}
捕获(ObjectNotFoundException)
{
data.Status=Status.Failed;
//通知用户找不到对象;
}
catch(WebException-we)
{
data.Status=Status.Failed;
//我想取消发送到管道的整批作业。
}
返回数据;
},新的ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=1});
var actionBlock=新的actionBlock((数据)=>
{
if(data.Status!=Status.Failed)
{
//处理数据
}
});
downloadBlock.LinkTo(actionBlock);

假设有大量项目被推入管道,并且发生了永久性错误。需要一段时间,所有项目都将通过重试而没有任何成功。相反,我希望在达到某个重试计数阈值时停止处理它们。为了实现这一点,我可以将
CancellationTokenSource
传递给每个
,但我的目标是创建管道,直到我的应用程序终止。据我所知,如果我取消
我的管道就消失了。当然,我可以在
Execute()
内部将令牌传递给委托,但这只会在处理方法内部停止。关于如何使用Polly library实现这一点,有什么想法吗?

在评论部分的帮助下,我得出结论,答案非常简单-我只需将
CancellationToken
传递到
策略中。执行()
方法如下:

CancellationTokenSource cts = new CancellationTokenSource();
policy.Execute(p =>
{
    //ThisMethodMyThrowWebException();
    data.Status = Status.Completed;
}, cts.Token);

编辑: 根据其他用户的请求,我正在使用完整的代码示例更新我的答案,该示例尝试回答评论部分中的问题

这有点长,但为了解决
operationCancelleException
问题,我需要拿出逻辑来区分从方法引发的异常(因此应该重试)和由于用户决定取消作业而引发的异常

我还决定在
断路器
中加入额外的代码,因为没有它,我认为这个答案是不完整的。我们开始吧

private TransformBlock<ConstructData, ConstructData> _downloadBlock;
private AsyncCircuitBreakerPolicy _circuitBreaker;
//This flag will indicate to circuitBreaker and catch block
//that OperationCanceledException was induced by user and 
//therefore it should not be looked upon as worthy by circuitBreaker
//to decide when to open circuit.
private bool _downloadCanceledByUser;
private CancellationTokenSource _downloadCts;
private void CreatePipeline()
{
    var failedItemQueue = new Queue<ConstructData>();

    _circuitBreaker
        = Policy.Handle<WebException>()
                .Or<OperationCanceledException>(ex => !_downloadCanceledByUser)
                .CircuitBreakerAsync(5, TimeSpan.FromDays(5),
                 (exception, timespan) => { },
                 () =>
                 {
                     while (failedItemQueue.Count > 0)
                     {
                         _downloadBlock.Post(failedItemQueue.Dequeue());
                     }
                 });

    var retryPolicy = Policy.Handle<WebException>().Or<OperationCanceledException>()
        .WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(1));

    #region DownloadBlock
    _downloadBlock = new TransformBlock<ConstructData, ConstructData>(async (construct) =>
    {
        var downloadPolicy = retryPolicy.WrapAsync(circuitBreaker);
        try
        {
            await downloadPolicy.ExecuteAsync(async fp =>
            {
                //await MethodThatMayThrowWebException(_downloadCts.Token);
                //await MethodThatMayThrowOperationCanceledException(_downloadCts.Token);
                construct.Status = DownloadFileStatus.Downloaded;
            }, _downloadCts.Token);
        }

        catch (WebException ex)
        {
            construct.Status = DownloadFileStatus.Failed;
            //Here after 2 failed retries guaranteed by retryPolicy,
            //I repost failed item back to the queue. 
            _downloadBlock.Post(construct);
        }
        catch (OperationCanceledException)
        {
            construct.Status = DownloadFileStatus.Canceled;
            //In case if OperationCanceledException was thrown from 
            //MethodThatMayThrowOperationCanceledException()
            //item gets posted back to downloadBlock for retry.
            if (!_downloadCanceledByUser)
                _downloadBlock.Post(construct);
        }
        //When total exception count reaches circuitBreaker threshold, circuit is left in open state
        catch (BrokenCircuitException ex)
        {
            //TODO Notify user that he needs to check internet connection and press reload.
            construct.Status = DownloadFileStatus.Failed;
            failedItemQueue.Enqueue(construct);
        }
        return construct;
    },
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1 });
    #endregion DownloadBlock

    #region ProcessBlock

    var processBlock = new ActionBlock<ConstructData>(construct =>
    {
        if (construct.Status == DownloadFileStatus.Downloaded)
        {
            var fullName = string.Concat(construct.Path, construct.Name);
            try
            {
                //ProcessingMethod(_downloadCts.Token);
            }
            catch (OperationCanceledException ex)
            {
                //TODO Do logging if needed.
            }
        }
    }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 1 });
    #endregion

    _downloadBlock.LinkTo(processBlock);
}

public void PushDataIntoPipeline(List<ConstructData> data)
{
    //Reset properties if previous batch was canceled
    if (_downloadCanceledByUser)
    {
        _downloadCts = new CancellationTokenSource();
        _downloadCanceledByUser = false;
    }
    //Push data into pipeline
    _ = data.Select(downloadBlock.SendAsync).ToList();
}
public void CancelData()
{
    _downloadCanceledByUser = true;
    _downloadCts.Cancel();
}
public void Reload()
{
    _circuitBreaker.Reset();
}
private TransformBlock\u downloadBlock;
专用异步断路器策略\u断路器;
//该标志将指示断路器和挡块
//该OperationCanceledException是由用户和
//因此,断路器不应将其视为有价值的
//决定何时开路。
私人bool_下载被用户取消;
private CancellationTokenSource下载;
私有void CreatePipeline()
{
var failedItemQueue=新队列();
_断路器
=Policy.Handle()
.或(ex=>!\u下载用户取消)
.CircuitBreakerAsync(5,时间跨度从天算起(5),
(例外情况,timespan)=>{},
() =>
{
while(failedItemQueue.Count>0)
{
_downloadBlock.Post(failedItemQueue.Dequeue());
}
});
var retryPolicy=Policy.Handle()或()
.WaitAndRetryAsync(2,retrytry=>TimeSpan.FromSeconds(1));
#区域下载块
_downloadBlock=新TransformBlock(异步(构造)=>
{
var downloadPolicy=retryPolicy.WrapAsync(断路器);
尝试
{
等待下载策略。ExecuteAsync(异步fp=>
{
//等待可能通过WebException(_downloadCts.Token)的方法;
//可通过操作取消异常(_downloadCts.Token)等待的方法;
construct.Status=DownloadFileStatus.download;
},_downloadCts.Token);
}
捕获(WebException ex)
{
construct.Status=DownloadFileStatus.Failed;
//在retryPolicy保证的2次失败重试后,
//我将失败的项目重新发布回队列。
_下载block.Post(构造);
}
捕获(操作取消异常)
{
construct.Status=DownloadFileStatus.cancelled;
//如果从中抛出OperationCanceledException
//方法可以通过以下操作取消异常()
//项目被发回downloadBlock以重试。
如果(!\u下载用户取消)
_下载block.Post(构造);
}
//当总异常计数达到断路器阈值时,电路处于断开状态
捕获(断开回路异常除外)
{
//TODO通知用户需要检查internet连接并按重新加载。
construct.Status=DownloadFileStatus.Failed;
failedItemQueue.Enqueue(构造);
}
返回结构;
},
新的ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=1});
#端域下载块
#区域处理块
var processBlock=新操作块(构造=>
{
if(construct.Status==DownloadFileStatus.download)
{
var fullName=string.Concat(construct.Path,construct.Name);
尝试
{
//处理方法(_downloadCts.Token);
}