You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 6 HttpClient基于路径的重复并行请求处理咨询

解决.NET Core 6中并行重复API请求的问题

针对你的场景,国内可以关注博客园、掘金的.NET技术板块,国外Stack Overflow本身就是非常合适的.NET技术社区。下面是你要的两种方案实现:

方案1:基于路径检测重复请求,等待首个请求完成后再查缓存

核心思路是用并发安全字典+信号量标记正在处理的请求路径,让后续相同路径的请求等待首个请求完成并写入缓存后,再从缓存读取结果,避免重复调用上游API。

实现代码

using System.Collections.Concurrent;
using Microsoft.Extensions.Caching.Memory;

public class ApiRequestHandler
{
    private readonly HttpClient _httpClient;
    private readonly IMemoryCache _cache;
    // 存储每个请求路径对应的信号量,控制并发等待
    private readonly ConcurrentDictionary<string, SemaphoreSlim> _requestLocks = new();

    public ApiRequestHandler(HttpClient httpClient, IMemoryCache cache)
    {
        _httpClient = httpClient;
        _cache = cache;
    }

    public async Task<HttpResponseMessage> GetApiResponseAsync(string requestPath)
    {
        // 先尝试从缓存读取
        if (_cache.TryGetValue(requestPath, out HttpResponseMessage cachedResponse))
        {
            return cachedResponse;
        }

        // 获取当前路径的信号量,不存在则创建(初始计数1)
        var semaphore = _requestLocks.GetOrAdd(requestPath, _ => new SemaphoreSlim(1, 1));
        try
        {
            await semaphore.WaitAsync();

            // 再次检查缓存:避免首个请求完成后,后续等待的请求重复调用API
            if (_cache.TryGetValue(requestPath, out cachedResponse))
            {
                return cachedResponse;
            }

            // 调用上游API
            var response = await _httpClient.GetAsync(requestPath);
            response.EnsureSuccessStatusCode();

            // 写入缓存(过期时间按需调整)
            _cache.Set(requestPath, response, TimeSpan.FromMinutes(5));

            return response;
        }
        finally
        {
            semaphore.Release();
            // 可选:请求完成后移除信号量,避免字典无限增长
            _requestLocks.TryRemove(requestPath, out _);
        }
    }
}

关键点说明

  • ConcurrentDictionary保证多线程下安全访问信号量
  • 两次检查缓存:第一次进入方法时,第二次获取信号量后,防止首个请求写入缓存后,后续请求仍重复调用API
  • SemaphoreSlim.WaitAsync()实现异步等待,不会阻塞线程

方案2:返回首个请求的响应任务(类似Java Future)

核心思路是把正在执行的请求任务本身存入并发字典,后续相同请求直接await这个任务,自动复用首个请求的响应结果,无需修改现有缓存逻辑,侵入性极低。

实现代码

using System.Collections.Concurrent;
using Microsoft.Extensions.Caching.Memory;

public class ApiRequestHandler
{
    private readonly HttpClient _httpClient;
    private readonly IMemoryCache _cache;
    // 存储每个请求路径对应的请求任务
    private readonly ConcurrentDictionary<string, Task<HttpResponseMessage>> _pendingRequests = new();

    public ApiRequestHandler(HttpClient httpClient, IMemoryCache cache)
    {
        _httpClient = httpClient;
        _cache = cache;
    }

    public async Task<HttpResponseMessage> GetApiResponseAsync(string requestPath)
    {
        // 先尝试从缓存读取
        if (_cache.TryGetValue(requestPath, out HttpResponseMessage cachedResponse))
        {
            return cachedResponse;
        }

        // 尝试获取或创建请求任务
        var requestTask = _pendingRequests.GetOrAdd(requestPath, async path =>
        {
            try
            {
                var response = await _httpClient.GetAsync(path);
                response.EnsureSuccessStatusCode();
                // 写入缓存
                _cache.Set(path, response, TimeSpan.FromMinutes(5));
                return response;
            }
            finally
            {
                // 请求完成后移除任务,避免内存泄漏
                _pendingRequests.TryRemove(path, out _);
            }
        });

        // 等待任务完成,不管是自己创建的还是复用的
        return await requestTask;
    }
}

关键点说明

  • ConcurrentDictionary<string, Task<HttpResponseMessage>>存储正在执行的请求任务,后续请求直接复用
  • 任务完成后自动移除,避免字典无限增长
  • 完全兼容现有缓存逻辑,只需包装请求调用部分,几乎不需要改动原有业务实现

内容的提问来源于stack exchange,提问作者Prasad.CH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 23:15:35