如何并行执行返回不同类型结果的电商API请求方法?
电商API请求并行执行解决方案
我正在开发一款对接电商API的应用,已经写了大概30个对应API请求的类,现在想实现这些请求的并行执行。之前试过用List<Task>来做,但因为方法返回类型不明确,没成功。
示例请求类
public class GetOrderStatusList : IRequest<GetOrderStatusList.Response> { public class Status { [JsonPropertyName("id")] public int Id { get; set; } [JsonPropertyName("name")] public string Name { get; set; } } public class Response : Output { [JsonPropertyName("statuses")] public List<Status> Statuses { get; set; } } }
public class GetProductsPrices : IRequest<GetProductsPrices.Response> { [JsonPropertyName("storage_id")] public string StorageId { get; set; } public class Product { public class Variant { [JsonPropertyName("variant_id")] public int VariantId { get; set; } [JsonPropertyName("price")] public decimal Price { get; set; } } [JsonPropertyName("product_id")] public int ProductId { get; set; } [JsonPropertyName("price")] public decimal Price { get; set; } [JsonPropertyName("variants")] public List<Variant> Variants { get; set; } } public class Response : Output { [JsonPropertyName("storage_id")] public string StorageId { get; set; } [JsonPropertyName("products")] public List<Product> Products { get; set; } } }
基础接口与请求发送方法
public interface IRequest<TResponse> { } public class Output { [JsonPropertyName("status")] public string Status { get; set; } [JsonPropertyName("error_message")] public string? ErrorMessage { get; set; } [JsonPropertyName("error_code")] public string? ErrorCode { get; set; } } public async Task<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> userRequest) where TResponse : Output { var client = new RestClient(_url); var method = GetRequestMethodName(userRequest); var request = CreateRequest(method, userRequest); var response = await ExecuteRequestAsync(client, request); var serializedResponse = JsonSerializer.Deserialize<TResponse>(response.Content); if( serializedResponse.Status == "ERROR") { throw new BaselinkerException(serializedResponse.ErrorMessage, serializedResponse.ErrorCode); } return serializedResponse; }
实现并行执行的具体方案
1. 统一任务类型,基于Output抽象
所有请求的Response都继承自Output,因此可以将所有SendRequestAsync调用包装为Task<Output>,统一放入列表管理:
// 构造请求实例列表 var requestInstances = new List<object> { new GetOrderStatusList(), new GetProductsPrices { StorageId = "your-storage-id" } }; // 转换为统一的Task<Output>列表 var tasks = requestInstances.Select(req => { return SendGenericRequest(req); }).ToList(); // 等待所有任务完成 await Task.WhenAll(tasks); // 逐个处理结果,转换为具体Response类型 foreach (var task in tasks) { var result = await task; if (result is GetOrderStatusList.Response statusResponse) { // 处理订单状态数据 foreach (var status in statusResponse.Statuses) { Console.WriteLine($"状态ID: {status.Id}, 名称: {status.Name}"); } } else if (result is GetProductsPrices.Response priceResponse) { // 处理商品价格数据 foreach (var product in priceResponse.Products) { Console.WriteLine($"商品ID: {product.ProductId}, 价格: {product.Price}"); } } }
2. 实现泛型辅助方法SendGenericRequest
需要通过反射调用泛型的SendRequestAsync,因为每个请求的TResponse类型不同:
private async Task<Output> SendGenericRequest(object request) { var requestType = request.GetType(); // 获取IRequest<TResponse>接口 var requestInterface = requestType.GetInterfaces() .First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequest<>)); // 提取Response类型 var responseType = requestInterface.GetGenericArguments()[0]; // 反射调用SendRequestAsync<TResponse> var sendMethod = typeof(YourApiClientClass) // 替换为你的类名 .GetMethod(nameof(SendRequestAsync)) .MakeGenericMethod(responseType); var task = (Task<Output>)sendMethod.Invoke(this, new[] { request }); return await task; }
3. 可选:带类型标记的任务列表(无需反射创建任务)
如果不想在创建任务时用反射,可以提前绑定任务和对应的Response类型:
var taskWithTypes = new List<(Task Task, Type ResponseType)> { (SendRequestAsync(new GetOrderStatusList()), typeof(GetOrderStatusList.Response)), (SendRequestAsync(new GetProductsPrices { StorageId = "your-storage-id" }), typeof(GetProductsPrices.Response)) }; // 等待所有任务完成 await Task.WhenAll(taskWithTypes.Select(t => t.Task)); // 处理结果 foreach (var (task, responseType) in taskWithTypes) { // 提取任务结果 var result = (Output)typeof(Task) .GetMethod("Result") .MakeGenericMethod(responseType) .Invoke(task, null); // 后续类型判断与处理逻辑和之前一致 }
4. 关键注意事项
- 限流控制:电商API通常有请求频率限制,用
SemaphoreSlim控制并行数:
var semaphore = new SemaphoreSlim(5); // 同时最多执行5个请求 var tasks = requestInstances.Select(async req => { await semaphore.WaitAsync(); try { return await SendGenericRequest(req); } finally { semaphore.Release(); } }).ToList();
- 异常处理:
Task.WhenAll会在任一任务抛出异常时立即终止,建议逐个检查任务状态或者在SendRequestAsync中捕获部分异常,避免单个请求失败导致全部任务中断。
内容的提问来源于stack exchange,提问作者newbieUnicorn
相关产品推荐
相关产品推荐

