Web服务的性能/吞吐量的异步方法

时间:2015-12-17 00:04:00

标签: c# performance web-services async-await

我试图弄清楚当使用HttpClient POST到外部api时,使用aysnc / await是否有助于应用程序吞吐量。

场景:我有一个类将POST数据发送到支付处理器web api。发布付款有4个步骤: 1 - POST联系人 2 - POST交易 3 - 后捐款 4 - POST信用卡付款

步骤1 - 4必须按照上面指定的顺序进行。

我的申请没有任何繁忙的工作"在等待来自支付处理器的响应时要做 - 在这种情况下,使用async / await进行下面的操作有意义吗?是否会在高容量期间提高应用程序吞吐量谢谢!

编辑:(问题标记为不清楚) 1.我的应用程序是web api(微服务) 2.我使用.Result(阻塞)来避免异步/等待(显然这是错误的!) 我们将有"秒杀"载重量为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;
    }

3 个答案:

答案 0 :(得分:1)

首先,编写代码的方式根本没有用,因为您通过调用Result来阻止所有时间。如果这是一件好事,为什么所有的API都不会在内部为你做这件事?!你不能用异步作弊......

如果超过线程范围100的线程池的功能,您将只看到吞吐量增加。

所需的平均线程数为requestsPerSecond * requestDurationInSeconds。插入一些数字,看看这对你来说是否真实。

我会将关于是同步还是异步的标准帖子链接到您,因为我觉得您不能完全清楚异步IO何时合适。

https://stackoverflow.com/a/25087273/122718为什么EF 6教程使用异步调用? https://stackoverflow.com/a/12796711/122718我们应该切换到默认使用异步I / O吗?

通常,当等待时间很长并且有许多并行请求在运行时,这是合适的。

  

我的申请没有任何繁忙的工作&#34;在等待回复时要做的事

其他请求进来是如此忙碌的工作。

请注意,当线程被阻塞时,CPU也不会被阻止。另一个线程可以运行。

答案 1 :(得分:1)

不能在这里实际使用await Task.WhenAll,因为当您购买下一个异步操作时,依赖于前一个的结果。因此,需要让它们以序列化方式执行。但是,仍然强烈建议您将async / await用于此类I / O,即;网络服务电话。

代码是在消耗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;
}

答案 2 :(得分:0)

当您进行异步/等待时,您应该全天异步。 阅读Async/Await - Best Practices in Asynchronous Programming

你需要让它们返回异步

 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);

编辑:误读OP。不要使用等待Task.WhenAll进行同步调用