如何在并发中给 HttpClient 设置不同的超时时间?

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在并发中给 HttpClient 设置不同的超时时间?相关的知识,希望对你有一定的参考价值。

咨询区

  • boot4life

为了能够复用 HttpClient 所打开的TCP链接,我不得不让所有的request共享一个单例,但问题来了,如果我简单的在每一次request之前修改 timeout 属性的话,会存在下面两个问题。

  1. 线程不安全。

  2. 并发时可能会错用到其他 request 的超时配置。

请问我该如何正确的处理?

回答区

  • Todd Menier

你说的这种场景理论上是不可以设置 Timeout 属性,但不要忘了,你可以用 .NET 库中提供的取消机制类 CancellationTokenSource,你可以在每一次 request 中带入 token 即可,然后用 CancelAfter() 方法来实现 Timeout 的功能,参考如下代码:

static async Task Main(string[] args)
        
            var httpClient = new HttpClient();
            var cts = new CancellationTokenSource();
            cts.CancelAfter(TimeSpan.FromSeconds(30));
            await httpClient.GetAsync("http://www.google.com", cts.Token);
        

哦,对了, HttpClient 的默认超时时间是 100s,如果你在 CancelAfter 中设置了过大的超时时间,本质上来说并没有效果,为了解决这个 Max 的问题,我建议你给 HttpClient 设置一个无限期的超时,参考代码如下:

static async Task Main(string[] args)
        
            var httpClient = new HttpClient();
            httpClient.Timeout = System.Threading.Timeout.InfiniteTimeSpan;

            var cts = new CancellationTokenSource();
            cts.CancelAfter(TimeSpan.FromSeconds(30));
            await httpClient.GetAsync("http://www.google.com", cts.Token);
        
  • Brad Albright

楼上的大佬说的非常好,但在我的场景下是这么实现的,首先我有一个默认的 CancellationToken ,它可被用户手工干预,另外还有一个 CancellationToken,它是为了模拟 timeout 的 CancelAfter ,然后通过 CancellationTokenSource.CreateLinkedTokenSource 来将两者结合起来,就可以做到既可以被 timeout 结束也可以被用户手工干预停止,下面是我的案例代码:

public async static Task<HttpResponseMessage> SendRequest(CancellationToken cancellationToken)

    var ctsForTimeout = new CancellationTokenSource();
    ctsForTimeout.CancelAfter(TimeSpan.FromSeconds(5));
    var cancellationTokenForTimeout = ctsForTimeout.Token;

    using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cancellationTokenForTimeout))
    
        try
        
            return await httpClient.GetAsync("http://asdfadsf", linkedCts.Token);
        
        catch
        
            //just for illustration purposes
            if (cancellationTokenForTimeout.IsCancellationRequested)
            
                Console.WriteLine("timeout");
            
            else if (cancellationToken.IsCancellationRequested)
            
                Console.WriteLine("other cancellation token cancelled");
            
            throw;
        
    

点评区

说实话,用 CancellationToken 来替代 Timeout 机制确实是一个非常好的想法,如果大家细心的话,会发现 Framework 中很多涉及IO的操作都实现了这种 取消机制,值得大家好好研究学习,比如下面代码:

public Task<HttpResponseMessage> DeleteAsync(Uri? requestUri, CancellationToken cancellationToken);
public Task<byte[]> GetByteArrayAsync(string? requestUri, CancellationToken cancellationToken);
public Task<Stream> GetStreamAsync(Uri? requestUri, CancellationToken cancellationToken);
public Task<string> GetStringAsync(string? requestUri, CancellationToken cancellationToken);
public Task<HttpResponseMessage> PutAsync(Uri? requestUri, HttpContent content, CancellationToken cancellationToken);
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken);

以上是关于如何在并发中给 HttpClient 设置不同的超时时间?的主要内容,如果未能解决你的问题,请参考以下文章

httpclient在并发量较高的调用下问题如何去

HttpClient在高并发场景下的优化实战

重新使用 HttpClient 但每个请求的超时设置不同?

将 HttpClient 设置为太短的超时会导致进程崩溃

如何解决高并发秒杀的超卖问题

C# HttpClient使用和注意事项,.NET Framework连接池并发限制