|
|
namespace Polly8Study.Test
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// Polly8超时策略 测试
|
|
|
/// 关键:
|
|
|
/// CancellationToken,没有这个是不起使用的
|
|
|
/// 就是之前版本中的乐观超时,悲观超时貌似取消了
|
|
|
/// </summary>
|
|
|
public class Polly8TimeoutStrategyTest
|
|
|
{
|
|
|
private readonly ITestOutputHelper _output;
|
|
|
|
|
|
public Polly8TimeoutStrategyTest(ITestOutputHelper testOutput)
|
|
|
{
|
|
|
_output = testOutput;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 超时策略选项
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void TimeoutStrategyOptions_Test()
|
|
|
{
|
|
|
//直接设置
|
|
|
new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(TimeSpan.FromMilliseconds(100))
|
|
|
.Build();
|
|
|
|
|
|
//默认值
|
|
|
var defaultOption = new TimeoutStrategyOptions();
|
|
|
|
|
|
//全功能
|
|
|
TimeoutStrategyOptions timeoutOption = new TimeoutStrategyOptions
|
|
|
{
|
|
|
//名称唯一标识了特定策略的特定实例,也包含在由各个弹性策略生成的遥测数据中
|
|
|
Name = "timeout",
|
|
|
|
|
|
//固定超时时间(启用了TimeoutGenerator,则无效)
|
|
|
Timeout = TimeSpan.FromSeconds(2),
|
|
|
|
|
|
//动态超时时间设置
|
|
|
//TimeoutGenerator = arg =>
|
|
|
//{
|
|
|
// var ts = TimeSpan.FromSeconds(Math.Pow(2, 1));
|
|
|
|
|
|
// return ValueTask.FromResult(ts);
|
|
|
//},
|
|
|
|
|
|
//发生超时时引发的超时委托
|
|
|
OnTimeout = (args) =>
|
|
|
{
|
|
|
var key = args.Context.OperationKey;
|
|
|
_output.WriteLine("OnTimeout");
|
|
|
return ValueTask.CompletedTask;
|
|
|
},
|
|
|
};
|
|
|
|
|
|
//使用
|
|
|
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(timeoutOption)
|
|
|
.Build();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 非可取消任务:忽略超时
|
|
|
/// 同步执行
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void No_CancellationToken_Test()
|
|
|
{
|
|
|
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(TimeSpan.FromSeconds(1))
|
|
|
.Build();
|
|
|
|
|
|
//外部tcs可省略
|
|
|
var tcs = new CancellationTokenSource();
|
|
|
try
|
|
|
{
|
|
|
pipeline.Execute
|
|
|
(
|
|
|
//此处必须是可取消的任务,否则超时不作为(一直等待)
|
|
|
callback: (CancellationToken innerToken) =>
|
|
|
{
|
|
|
//虽然接口耗时大,因为没有使用‘可取消的’ Send请求,超时设置被忽略,会等待接口正确返回
|
|
|
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost:44344/api/Timeout/Slow");
|
|
|
|
|
|
//关键:Send方法 没有传入参数 cancellationToken
|
|
|
//其它同步方法,只要是可取消方法均可
|
|
|
HttpResponseMessage response = new HttpClient().Send(requestMessage);
|
|
|
if (response.IsSuccessStatusCode == false)
|
|
|
{
|
|
|
_output.WriteLine($"非正常响应,响应码:{response.StatusCode}");
|
|
|
return;
|
|
|
}
|
|
|
var stream = response.Content.ReadAsStream();
|
|
|
var bs = new byte[stream.Length];
|
|
|
stream.Read(bs, 0, (int)stream.Length);
|
|
|
|
|
|
var text = System.Text.UTF8Encoding.UTF8.GetString(bs);
|
|
|
|
|
|
_output.WriteLine($"响应内容:{text}");
|
|
|
|
|
|
_output.WriteLine("任务执行完成");
|
|
|
},
|
|
|
cancellationToken: tcs.Token
|
|
|
);
|
|
|
}
|
|
|
catch (OperationCanceledException ex)
|
|
|
{
|
|
|
_output.WriteLine($"任务取消,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (TimeoutRejectedException ex)
|
|
|
{
|
|
|
_output.WriteLine($"超时,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
_output.WriteLine($"API服务异常:{ex.Message}");
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
tcs.TryReset();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 可取消任务:超时
|
|
|
/// 同步执行
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void Has_CancellationToken_Timeout_Test()
|
|
|
{
|
|
|
TimeoutStrategyOptions timeoutOption = new TimeoutStrategyOptions
|
|
|
{
|
|
|
//名称唯一标识了特定策略的特定实例,也包含在由各个弹性策略生成的遥测数据中
|
|
|
Name = "timeout",
|
|
|
|
|
|
//固定超时时间(启用了TimeoutGenerator,则无效)
|
|
|
Timeout = TimeSpan.FromSeconds(1),
|
|
|
|
|
|
//动态超时时间设置
|
|
|
//TimeoutGenerator = arg =>
|
|
|
//{
|
|
|
// var ts = TimeSpan.FromSeconds(Math.Pow(2, 1));
|
|
|
|
|
|
// return ValueTask.FromResult(ts);
|
|
|
//},
|
|
|
|
|
|
//发生超时时引发的超时委托
|
|
|
OnTimeout = (args) =>
|
|
|
{
|
|
|
_output.WriteLine("OnTimeout 超时委托执行");
|
|
|
return ValueTask.CompletedTask;
|
|
|
},
|
|
|
};
|
|
|
|
|
|
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(timeoutOption)
|
|
|
.Build();
|
|
|
|
|
|
var tcs = new CancellationTokenSource();
|
|
|
try
|
|
|
{
|
|
|
pipeline.Execute
|
|
|
(
|
|
|
//此处必须是可取消的任务,否则超时不作为(一直等待)
|
|
|
callback: (innerToken) =>
|
|
|
{
|
|
|
HttpRequestMessage r = new HttpRequestMessage(HttpMethod.Get, "http://localhost:44344/api/Timeout/Slow");
|
|
|
var response = new HttpClient().Send(r, innerToken);
|
|
|
if (response.IsSuccessStatusCode == false)
|
|
|
{
|
|
|
Console.WriteLine("非正常响应");
|
|
|
return;
|
|
|
}
|
|
|
var stream = response.Content.ReadAsStream();
|
|
|
var bs = new byte[stream.Length];
|
|
|
stream.Read(bs, 0, (int)stream.Length);
|
|
|
|
|
|
var text = System.Text.UTF8Encoding.UTF8.GetString(bs);
|
|
|
|
|
|
_output.WriteLine($"响应内容:{text}");
|
|
|
_output.WriteLine("任务执行完成");
|
|
|
},
|
|
|
cancellationToken: tcs.Token
|
|
|
);
|
|
|
}
|
|
|
catch (OperationCanceledException ex)
|
|
|
{
|
|
|
_output.WriteLine($"任务取消,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (TimeoutRejectedException ex)
|
|
|
{
|
|
|
_output.WriteLine($"超时,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
_output.WriteLine($"API服务异常:{ex.Message}");
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
tcs.TryReset();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 可取消任务:不超时
|
|
|
/// 同步执行
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public void Has_CancellationToken_NotTimeout_Test()
|
|
|
{
|
|
|
TimeoutStrategyOptions timeoutOption = new TimeoutStrategyOptions
|
|
|
{
|
|
|
//名称唯一标识了特定策略的特定实例,也包含在由各个弹性策略生成的遥测数据中
|
|
|
Name = "timeout",
|
|
|
|
|
|
//固定超时时间(启用了TimeoutGenerator,则无效)
|
|
|
Timeout = TimeSpan.FromSeconds(5),
|
|
|
|
|
|
//动态超时时间设置
|
|
|
//TimeoutGenerator = arg =>
|
|
|
//{
|
|
|
// var ts = TimeSpan.FromSeconds(Math.Pow(2, 1));
|
|
|
|
|
|
// return ValueTask.FromResult(ts);
|
|
|
//},
|
|
|
|
|
|
//发生超时时引发的超时委托
|
|
|
OnTimeout = (args) =>
|
|
|
{
|
|
|
Console.WriteLine("OnTimeout 超时委托执行");
|
|
|
return ValueTask.CompletedTask;
|
|
|
},
|
|
|
};
|
|
|
|
|
|
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(timeoutOption)
|
|
|
.Build();
|
|
|
|
|
|
var tcs = new CancellationTokenSource();
|
|
|
try
|
|
|
{
|
|
|
pipeline.Execute
|
|
|
(
|
|
|
//此处必须是可取消的任务,否则超时不作为(一直等待)
|
|
|
callback: (innerToken) =>
|
|
|
{
|
|
|
HttpRequestMessage r = new HttpRequestMessage(HttpMethod.Get, "http://localhost:44344/api/Timeout/Slow");
|
|
|
var response = new HttpClient().Send(r, innerToken);
|
|
|
if (response.IsSuccessStatusCode == false)
|
|
|
{
|
|
|
Console.WriteLine("非正常响应");
|
|
|
return;
|
|
|
}
|
|
|
var stream = response.Content.ReadAsStream();
|
|
|
var bs = new byte[stream.Length];
|
|
|
stream.Read(bs, 0, (int)stream.Length);
|
|
|
|
|
|
var text = System.Text.UTF8Encoding.UTF8.GetString(bs);
|
|
|
|
|
|
_output.WriteLine($"响应内容:{text}");
|
|
|
_output.WriteLine("任务执行完成");
|
|
|
},
|
|
|
cancellationToken: tcs.Token
|
|
|
);
|
|
|
}
|
|
|
catch (OperationCanceledException ex)
|
|
|
{
|
|
|
_output.WriteLine($"任务取消,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (TimeoutRejectedException ex)
|
|
|
{
|
|
|
_output.WriteLine($"超时,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
_output.WriteLine($"API服务异常:{ex.Message}");
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
tcs.TryReset();
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 不可取消任务:忽略超时
|
|
|
/// 异步执行
|
|
|
/// </summary>
|
|
|
[Fact]
|
|
|
public async void No_CancellationToken_ExecuteAsync_Test()
|
|
|
{
|
|
|
TimeoutStrategyOptions timeoutOption = new TimeoutStrategyOptions
|
|
|
{
|
|
|
//名称唯一标识了特定策略的特定实例,也包含在由各个弹性策略生成的遥测数据中
|
|
|
Name = "timeout",
|
|
|
|
|
|
//固定超时时间(启用了TimeoutGenerator,则无效)
|
|
|
Timeout = TimeSpan.FromSeconds(1),
|
|
|
|
|
|
|
|
|
//发生超时时引发的超时委托
|
|
|
OnTimeout = (args) =>
|
|
|
{
|
|
|
_output.WriteLine("OnTimeout 超时委托执行");
|
|
|
return ValueTask.CompletedTask;
|
|
|
},
|
|
|
};
|
|
|
|
|
|
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(timeoutOption)
|
|
|
.Build();
|
|
|
|
|
|
var tcs = new CancellationTokenSource();
|
|
|
try
|
|
|
{
|
|
|
await pipeline.ExecuteAsync
|
|
|
(
|
|
|
//此处为不可取消的任务,忽略超时(会一直等待)
|
|
|
callback: async (innerToken) =>
|
|
|
{
|
|
|
//完成大约是2秒多,而不是超时的1秒多
|
|
|
await Task.Delay (1000*3);
|
|
|
_output.WriteLine("任务执行完成");
|
|
|
},
|
|
|
cancellationToken: tcs.Token
|
|
|
);
|
|
|
}
|
|
|
catch (OperationCanceledException ex)
|
|
|
{
|
|
|
_output.WriteLine($"任务取消,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (TimeoutRejectedException ex)
|
|
|
{
|
|
|
_output.WriteLine($"超时,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
_output.WriteLine($"API服务异常:{ex.Message}");
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
tcs.TryReset();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 可取消任务:超时
|
|
|
/// 异步执行
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[Fact]
|
|
|
public async Task Has_CancellationToken_ExecuteAsync_Timeout_Test()
|
|
|
{
|
|
|
var pipeline = new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(TimeSpan.FromSeconds(1))
|
|
|
.Build();
|
|
|
|
|
|
await Assert.ThrowsAnyAsync<Exception>(async () =>
|
|
|
{
|
|
|
var cts = new CancellationTokenSource();
|
|
|
await pipeline.ExecuteAsync
|
|
|
(
|
|
|
callback: async (innerToken) =>
|
|
|
{
|
|
|
//完成大约是超时的1秒多,而不是任务的3秒多
|
|
|
await Task.Delay(1000 * 3, innerToken);
|
|
|
_output.WriteLine("任务执行完成");
|
|
|
},
|
|
|
cancellationToken: cts.Token
|
|
|
);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 可取消任务:不超时
|
|
|
/// 异步执行
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[Fact]
|
|
|
public async Task Has_CancellationToken_ExecuteAsync_NoTimeout_Test()
|
|
|
{
|
|
|
var pipeline = new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(TimeSpan.FromSeconds(3))
|
|
|
.Build();
|
|
|
|
|
|
try
|
|
|
{
|
|
|
var cts = new CancellationTokenSource();
|
|
|
await pipeline.ExecuteAsync
|
|
|
(
|
|
|
callback: async (innerToken) =>
|
|
|
{
|
|
|
//不超时,完成大约是任务耗时的1秒
|
|
|
await Task.Delay(1000, innerToken);
|
|
|
_output.WriteLine("任务执行完成");
|
|
|
},
|
|
|
cancellationToken: cts.Token
|
|
|
);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
_output.WriteLine(ex.Message);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// <summary>
|
|
|
/// 可取消任务:超时
|
|
|
/// 异步执行,无外层Token
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[Fact]
|
|
|
public async Task Has_OuterCancellationToken_ExecuteAsync_Timeout_Test()
|
|
|
{
|
|
|
var pipeline = new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(TimeSpan.FromSeconds(1))
|
|
|
.Build();
|
|
|
|
|
|
|
|
|
await Assert.ThrowsAnyAsync<Exception>(async () =>
|
|
|
{
|
|
|
await pipeline.ExecuteAsync
|
|
|
(
|
|
|
callback: async (innerToken) =>
|
|
|
{
|
|
|
//完成大约是超时的1秒多,而不是任务的3秒多
|
|
|
await Task.Delay(1000 * 3, innerToken);
|
|
|
_output.WriteLine("任务执行完成");
|
|
|
}
|
|
|
);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
public void SyncWithCancellationToken_Test()
|
|
|
{
|
|
|
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
|
|
|
.AddTimeout(TimeSpan.FromSeconds(3))
|
|
|
.Build();
|
|
|
|
|
|
var tcs = new CancellationTokenSource();
|
|
|
try
|
|
|
{
|
|
|
pipeline.Execute
|
|
|
(
|
|
|
//此处必须是可取消的任务,否则超时不作为(一直等待)
|
|
|
callback: SyncWithCancellationToken,
|
|
|
cancellationToken: tcs.Token
|
|
|
);
|
|
|
}
|
|
|
catch (OperationCanceledException ex)
|
|
|
{
|
|
|
_output.WriteLine($"任务取消,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (TimeoutRejectedException ex)
|
|
|
{
|
|
|
_output.WriteLine($"超时,引发异常:{ex.Message}");
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
_output.WriteLine($"API服务异常:{ex.Message}");
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
tcs.TryReset();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 使用CancellationToken的同步方法
|
|
|
/// </summary>
|
|
|
private void SyncWithCancellationToken(CancellationToken cancellationToken)
|
|
|
{
|
|
|
Thread thread = new Thread((token) =>
|
|
|
{
|
|
|
int max = 500;
|
|
|
while (max > 0)
|
|
|
{
|
|
|
if (cancellationToken.IsCancellationRequested)
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
max -= 1;
|
|
|
Thread.Sleep(10);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
thread.Start();
|
|
|
thread.Join();
|
|
|
}
|
|
|
}
|
|
|
} |