web服务性能/吞吐量的异步方法
本文关键字:吞吐量 异步方法 性能 服务 服务性 web | 更新日期: 2023-09-27 18:27:09
我正试图弄清楚在使用HttpClient向外部api POST时,使用aysnc/await是否有助于提高应用程序吞吐量。
场景:我有一个类,将POST的数据发送到支付处理器的web api。POST付款有4个步骤:1-POST联系人2-POST事务3-捐赠后4-POST信用卡支付
步骤1-4必须按照上面指定的顺序进行。
在等待支付处理器的响应时,我的应用程序没有任何"繁忙的工作"要做——在这种情况下,使用async/await进行以下操作有意义吗?它会在高容量期间提高应用程序吞吐量吗?谢谢
编辑:(问题标记为不清楚)1.我的应用程序是一个web api(微服务)2.我使用.Result(阻塞)来避免异步/等待(显然这是错误的!)3.我们将有1000 req/分钟的"尖峰"负载
public virtual ConstituentResponse PostConstituent(Constituent constituent)
{
var response = PostToUrl<Constituent>("/api/Constituents", constituent);
if (!response.IsSuccessStatusCode)
HandleError(response);
return response.Content.ReadAsAsync<ConstituentResponse>().Result;
}
public virtual TransactionResponse PostTransaction(Transaction transaction)
{
var response = PostToUrl<Transaction>("/api/Transactions", transaction);
if (!response.IsSuccessStatusCode)
HandleError(response);
return response.Content.ReadAsAsync<TransactionResponse>().Result;
}
public virtual DonationResponse PostDonation(Donation donation)
{
var response = PostToUrl<Donation>("/api/Donations", donation);
if (!response.IsSuccessStatusCode)
HandleError(response);
return response.Content.ReadAsAsync<DonationResponse>().Result;
}
public virtual CreditCardPaymentResponse PostCreditCardPayment(CreditCardPayment creditCardPayment)
{
var response = PostToUrl<CreditCardPayment>("/api/CreditCardPayments", creditCardPayment);
if (!response.IsSuccessStatusCode)
HandleError(response);
return response.Content.ReadAsAsync<CreditCardPaymentResponse>().Result;
}
protected virtual HttpResponseMessage PostToUrl<T>(string methodUri, T value)
{
return _httpClient.PostAsJsonAsync(methodUri, value).Result;
}
上面的五个方法是从另一个类/函数调用的:
public virtual IPaymentResult Purchase(IDonationEntity donation, ICreditCard creditCard)
{
var constituentResponse = PostConstituent(donation);
var transactionResponse = PostTransaction(donation, constituentResponse);
var donationResponse = PostDonation(donation, constituentResponse, transactionResponse);
var creditCardPaymentResponse = PostCreditCardPayment(donation, creditCard, transactionResponse);
var paymentResult = new PaymentResult
{
Success = (creditCardPaymentResponse.Status == Constants.PaymentResult.Succeeded),
ExternalPaymentID = creditCardPaymentResponse.PaymentID,
ErrorMessage = creditCardPaymentResponse.ErrorMessage
};
return paymentResult;
}
您不能在此处实际使用await Task.WhenAll
,因为您在购买下一个异步操作时依赖于上一个操作的结果。因此,需要以序列化的方式执行它们。然而,仍然强烈建议您将async / await
用于此类I/O,即。;web服务调用。
该代码是在消耗Async*
方法调用的情况下编写的,但实际上并没有使用该模式——它是阻塞的,可能会导致死锁以及不希望的性能影响。您应该只在控制台应用程序中使用.Result
(和.Wait()
)。理想情况下,您应该使用async / await
。以下是调整代码的正确方法。
public virtual async Task<ConstituentResponse> PostConstituenAsync(Constituent constituent)
{
var response = await PostToUrlAsync<Constituent>("/api/Constituents", constituent);
if (!response.IsSuccessStatusCode)
HandleError(response);
return await response.Content.ReadAsAsync<ConstituentResponse>();
}
public virtual async Task<TransactionResponse PostTransactionAsync(Transaction transaction)
{
var response = await PostToUrl<Transaction>("/api/Transactions", transaction);
if (!response.IsSuccessStatusCode)
HandleError(response);
return await response.Content.ReadAsAsync<TransactionResponse>();
}
public virtual async Task<DonationResponse> PostDonationAsync(Donation donation)
{
var response = await PostToUrl<Donation>("/api/Donations", donation);
if (!response.IsSuccessStatusCode)
HandleError(response);
return await response.Content.ReadAsAsync<DonationResponse>();
}
public virtual async Task<CreditCardPaymentResponse> PostCreditCardPaymentAsync(CreditCardPayment creditCardPayment)
{
var response = await PostToUrlAsync<CreditCardPayment>("/api/CreditCardPayments", creditCardPayment);
if (!response.IsSuccessStatusCode)
HandleError(response);
return await response.Content.ReadAsAsync<CreditCardPaymentResponse>();
}
protected virtual Task<HttpResponseMessage> PostToUrlAsync<T>(string methodUri, T value)
{
return _httpClient.PostAsJsonAsync(methodUri, value);
}
用法
public virtual await Task<IPaymentResult> PurchaseAsync(IDonationEntity donation, ICreditCard creditCard)
{
var constituentResponse = await PostConstituentAsync(donation);
var transactionResponse = await PostTransactionAsync(donation, constituentResponse);
var donationResponse = await PostDonationAsync(donation, constituentResponse, transactionResponse);
var creditCardPaymentResponse = await PostCreditCardPaymentAsync(donation, creditCard, transactionResponse);
var paymentResult = new PaymentResult
{
Success = (creditCardPaymentResponse.Status == Constants.PaymentResult.Succeeded),
ExternalPaymentID = creditCardPaymentResponse.PaymentID,
ErrorMessage = creditCardPaymentResponse.ErrorMessage
};
return paymentResult;
}
首先,现在编写代码的方式根本没有帮助,因为您一直在通过调用Result
进行阻塞。如果这是一件好事,为什么不让所有API都在内部为您做这件事呢?!你不能用async作弊。。。
只有当您超过线程池的能力时,您才会看到吞吐量的提高,这种情况发生在100个线程的范围内。
所需的平均线程数为CCD_ 8。插入一些数字,看看这对你来说是否现实。
我会把我关于是同步还是异步的标准帖子链接给你,因为我觉得你不清楚什么时候异步IO是合适的。
https://stackoverflow.com/a/25087273/122718为什么EF 6教程使用异步调用?https://stackoverflow.com/a/12796711/122718我们是否应该默认切换为使用异步I/O?
一般来说,当等待时间很长并且有许多并行请求在运行时,这是合适的。
我的应用程序在等待响应时没有任何"繁忙的工作"要做
收到的其他请求都是繁忙的工作。
请注意,当线程被阻塞时,CPU也不会被阻塞。另一个线程可以运行。
当你进行async/await时,你应该全天异步。阅读Async/Await-异步编程的最佳实践
你需要让他们返回异步
public virtual async Task ConstituentResponse PostConstituent(Constituent constituent)
{
var response = PostToUrl<Constituent>("/api/Constituents", constituent);
if (!response.IsSuccessStatusCode)
HandleError(response);
return await response.Content.ReadAsAsync<ConstituentResponse>();
}
//...
//etc
然后从主功能
await Task.WhenAll(constituentResponse, transactionResponse, donationResponse, creditCardPaymentResponse);
编辑:读错操作。不要使用等待任务。WhenAll用于同步调用