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

ASP.NET MVC多API调用并行优化及异步异常问题咨询

ASP.NET MVC多供应商API调用异步优化方案

问题背景

对接10余家供应商API获取航班数据,原代码使用Task.WaitAll阻塞等待所有请求完成,超时导致用户体验差;尝试Task.WhenAny后出现视图无数据、错误未捕获的问题,需优化代码逻辑。

原代码核心问题

  1. 主线程阻塞:Task.WaitAll会阻塞当前线程,长时间等待引发请求超时,用户等待体验极差。
  2. 线程不安全集合:List<FlightDetail>非线程安全,多线程并发添加数据会导致数据丢失、异常,最终视图无数据。
  3. 异常处理缺失:仅捕获OperationCanceledException,API调用失败、网络错误等异常未处理,任务失败无记录,数据完整性无法保障。
  4. 参数共享风险:循环中复用copyRequest,后续任务可能拿到被修改后的请求参数,导致请求逻辑错误。
  5. 无单任务超时机制:单个供应商API响应慢会拖慢整体流程,没有超时熔断控制。

改进方案

关键优化点

  • 改用async/await非阻塞模式,替换Task.WaitAll为await Task.WhenAll,释放线程池资源。
  • 使用ConcurrentBag<FlightDetail>线程安全集合存储结果,避免并发修改冲突。
  • 为每个API调用添加独立超时控制,超时自动取消任务并记录日志。
  • 全量捕获任务异常,单个任务失败不影响其他任务执行。
  • 循环内为每个任务创建独立的请求参数副本,避免参数共享导致的逻辑错误。
  • 用Task.Run替代Task.Factory.StartNew,符合现代异步编程规范。

改进后完整代码

public async Task<List<string>> ApplySourcesAsync(List<string> sources, string suppliers, List<RNR> lstRnr, FlightSearchRequest rq, Client client,
    List<FlightDetail> lstflightdetails, List<PccMatrix> pccMatrixList, bool isdomesticreturn = false)
{
    List<string> lstsources = new List<string>();
    SortedDictionary<string, Airport> airports = _flyCache.Airports;
    List<Task> taskList = new List<Task>();
    // 线程安全集合存储多任务结果
    ConcurrentBag<FlightDetail> flightDetails = new ConcurrentBag<FlightDetail>();
    FLY.dal.Flight.Handlers.NearbyAirportsHandler nearbyAirportsHandler = new dal.Flight.Handlers.NearbyAirportsHandler();
    List<entities.Flight.NearbyAirports> nearbyAirports = nearbyAirportsHandler.GetNearbyAirportsActive();
    string[] flyapisources = new string[] { "FHIN", "FHBD", "FHAE", "FHTR", "FHMY", "FHSG" };

    foreach (var source in sources)
    {
        // 每个任务创建独立请求副本,避免参数共享修改
        FlightSearchRequest copyRequest = (FlightSearchRequest)FLY.Helper.Helper.DeepCloneusingJson(rq);
        if (string.IsNullOrWhiteSpace(rq.AirTravelPreference.AirlinePreference)
            && !string.IsNullOrWhiteSpace(copyRequest.AirTravelPreference.AirlinePreference))
        {
            copyRequest.AirTravelPreference.AirlinePreference = "";
        }

        IFlightsSuppliers supplier = null;
        if (!rq.IsBSAdvancedSearch)
        {
            if (source == "MY" && suppliers.Contains("MY"))
            {
                List<RNR> lstRNRsource = lstRnr.Where(a => a.Source == "ALL" || a.Source == "MY").ToList();
                if (RNRToAllowSearch(lstRNRsource, airports, copyRequest))
                    supplier = new MyFares.Mystifly();
            }
            if (source == "2S" && suppliers.Contains("2S"))
            {
                List<RNR> lstRNRsource = lstRnr.Where(a => a.Source == "ALL" || a.Source == "2S").ToList();
                if (RNRToAllowSearch(lstRNRsource, airports, copyRequest))
                    supplier = new SabreRest.Flights();
            }
            if (source == "1S" && suppliers.Contains("1S"))
            {
                List<RNR> lstRNRsource = lstRnr.Where(a => a.Source == "ALL" || a.Source == "1S").ToList();
                if (RNRToAllowSearch(lstRNRsource, airports, copyRequest, pccMatrixList))
                {
                    var filterednearbyairports = nearbyAirports?.Where(a => a.Source.Equals("1S")).ToList();
                    supplier = new Sabre.SabreAPI();
                }
            }
            // Singapore
            if (source == "SQ" && suppliers.Contains("SQ"))
            {
                List<RNR> lstRNRsource = lstRnr.Where(a => a.Source == "ALL" || a.Source == "SQ").ToList();
                bool airlineAllowed = string.IsNullOrWhiteSpace(copyRequest.AirTravelPreference.AirlinePreference)
                    || copyRequest.AirTravelPreference.AirlinePreference.ToUpper().Split(',').Contains("SQ");
                if (RNRToAllowSearch(lstRNRsource, airports, copyRequest) && airlineAllowed)
                    supplier = new SingaporeAirline.SingaporeAirlineAPI();
            }
        }

        if (supplier != null)
        {
            lstsources.Add(source);
            var newpccMatrixList = pccMatrixList.Where(a => a.IsAvailable).ToList();
            if (source.StartsWith("FH"))
            {
                newpccMatrixList = newpccMatrixList.Where(a => a.Pcc == source).ToList();
            }
            var copiednearbyairports = nearbyAirports?.Where(a => a.Source.Equals(source)).ToList();
            if (copiednearbyairports != null)
                copiednearbyairports = FLY.Helper.Helper.DeepCloneusingJson(copiednearbyairports);

            // 带超时控制的异步任务
            var task = Task.Run(async () =>
            {
                using (var cts = new CancellationTokenSource())
                {
                    // 单个任务超时设置为30秒,可根据实际调整
                    var timeoutTask = Task.Delay(TimeSpan.FromSeconds(30), cts.Token);
                    var apiTask = Task.Run(() =>
                    {
                        List<FlightDetail> tempDetails = new List<FlightDetail>();
                        supplier.Search(copyRequest, tempDetails, newpccMatrixList, client, copiednearbyairports);
                        // 将临时结果添加到线程安全集合
                        foreach (var detail in tempDetails)
                        {
                            flightDetails.Add(detail);
                        }
                    }, cts.Token);

                    var completedTask = await Task.WhenAny(apiTask, timeoutTask);
                    if (completedTask == timeoutTask)
                    {
                        cts.Cancel();
                        log.Error($"供应商[{source}] API调用超时");
                    }
                    else
                    {
                        await apiTask; // 确保API任务完成,捕获执行异常
                    }
                }
            });

            taskList.Add(task);
            pccMatrixList.ForEach(a => a.IsAvailable = false);
        }
    }

    try
    {
        if (taskList.Count > 0)
            await Task.WhenAll(taskList); // 非阻塞等待所有任务完成
    }
    catch (Exception ex)
    {
        log.Error($"批量API调用异常: {ex.Message}\n{ex.StackTrace}");
    }

    var resultFlightDetails = flightDetails.ToList();
    if (resultFlightDetails.Count == 0)
        return lstsources;

    if (!iscouponexecuted)
        flightBookingCouponDetails = new FlightBookCouponCodeProvider().GetBookingCoupons(rq, airports);
    var currentrequest = FLY.Helper.Helper.DeepCloneusingJson(rq);
    resultFlightDetails.ForEach(a => { a.FlightSearchRequest = currentrequest; a.BookingCouponDetails = flightBookingCouponDetails; });
    lstflightdetails.AddRange(resultFlightDetails);

    return lstsources;
}

优化效果说明

  1. 非阻塞异步:await Task.WhenAll释放线程池资源,提升系统并发能力,避免用户长时间等待。
  2. 数据安全:ConcurrentBag确保多线程环境下数据无丢失、无冲突,解决视图无数据问题。
  3. 超时控制:单个API任务超时自动取消,避免慢接口拖垮整体流程。
  4. 异常可控:全量捕获异常,单个任务失败不影响其他任务,日志记录便于问题排查。
  5. 参数隔离:每个任务使用独立请求副本,避免参数共享导致的逻辑错误。

内容的提问来源于stack exchange,提问作者Sagar Pujar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 01:05:00