C# 用于web服务性能/吞吐量的异步方法

C# 用于web服务性能/吞吐量的异步方法,c#,performance,web-services,async-await,C#,Performance,Web Services,Async Await,我试图弄清楚当使用HttpClient发布到外部api时,使用aysnc/await是否有助于提高应用程序吞吐量 场景:我有一个类,它将数据发布到支付处理器web api。有4个步骤可以过账付款: 1-邮政联系 2-交易后 3-捐款后 4-信用卡后付款 步骤1-4必须按上述顺序进行 我的应用程序在等待支付处理器的响应时没有任何“繁忙的工作”要做-在这种情况下,使用async/Wait进行以下操作有意义吗?它会在高容量时增加应用程序吞吐量吗?谢谢 编辑:(问题被标记为不清楚) 1.我的应用程序是一

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

场景:我有一个类,它将数据发布到支付处理器web api。有4个步骤可以过账付款: 1-邮政联系 2-交易后 3-捐款后 4-信用卡后付款

步骤1-4必须按上述顺序进行

我的应用程序在等待支付处理器的响应时没有任何“繁忙的工作”要做-在这种情况下,使用async/Wait进行以下操作有意义吗?它会在高容量时增加应用程序吞吐量吗?谢谢

编辑:(问题被标记为不清楚) 1.我的应用程序是一个web api(微服务) 2.我使用.Result(阻塞)来避免异步/等待(显然这是错误的!) 3.我们将有1000个请求/分钟的“峰值”负载

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

首先,现在编写代码的方式没有任何帮助,因为您一直在通过调用
Result
进行阻塞。如果这是一件好事,为什么所有API都不在内部为您这么做呢?!你不能用异步作弊

只有当您超过线程池的能力时,您才会看到吞吐量的提高,这种情况发生在100个线程范围内

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

我将把我关于是同步还是异步的标准帖子链接给你们,因为我觉得你们不太清楚什么时候异步IO是合适的

为什么EF 6教程使用非同步调用? 我们应该在默认情况下切换到使用异步I/O吗

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

我的应用程序在等待响应时没有任何“繁忙的工作”要做

进来的其他请求都是如此繁忙的工作


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

当您执行异步/等待时,您应该整天异步。 阅读

您需要使它们返回异步

 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

编辑:误读操作。不要使用wait Task.whall进行同步调用

不能实际使用
wait Task。whall
与您购买下一个异步操作时一样,依赖于上一个调用的结果。因此,您需要让它们以序列化的方式执行。但是,仍然强烈建议您对这样的I/O使用
async/await
,即:。;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);
}
公共虚拟异步任务构造后同步(构成)
{
var响应=等待后Tourlasync(“/api/成分”,成分);
如果(!response.issucessStatusCode)
HandleError(响应);
return wait response.Content.ReadAsAsync();
}

公共虚拟异步任务您的问题有点不清楚-如果您的应用程序在等待响应时没有“繁忙的工作”要做,那么您在哪里寻找性能提升?您需要显示调用
Purchase
的方式,以确定
async
是否有帮助-如果从单个线程串行调用
Purchase
,则多线程将有帮助,但如果从不同线程(即从web服务器)调用
Purchase
,我认为这不会有什么帮助,因为您的
…Async()。Result
阻塞调用将允许其他线程在等待结果时运行(正如@usr所指出的),在您没有正确使用
Async
时,运行“使用aysnc/wait是否有助于提高应用程序吞吐量”的实验可能没有多大意义。停止调用的
.Result()
one@user2521118我已经发布了一个答案,我希望我解释得足够好,并且正确地解决了您的担忧。如果没有,请告诉我。很有趣。。Task.WhenAll是否会按顺序执行这4个调用,因为调用依赖于上一步中调用的数据。回答很好,但是是的,因为OP的方法相互依赖,
Task.WhenAll()
可能不合适here@Micky是的,你说得对。我忽略了操作。是的,它们必须按顺序执行,以异步方式执行它们并没有真正的好处。Joordan,说得对@user2521118不清楚从客户端等待一系列相互依赖且连续的“异步”的角度来看,您会得到什么好处calls@Mickyjoordan831——这也是我写代码时的想法。“blocking”这个词让我害怕,但我最终使用了HttpClient类,因为它可以轻松地将业务对象序列化到JSON或从JSON序列化。。假设结果与同步代码相同是否安全?只要它不阻塞其他线程,一切都很好。。我们将通过更多的硬件/负载平衡进行扩展,以处理高容量。是的,其他请求也很重要,这就是我关心的原因。结果,并试图找出如何使用async/await。如果POST信用卡需要2秒钟,那么当前线程是否会被释放以处理其他请求?(使用async/await)是的,使用async和await进行所有操作。这一点很好!async/await实际上是一种模式,代码应该从请求级别/控制器级别修改为异步。。
  await Task.WhenAll(constituentResponse, transactionResponse, donationResponse, creditCardPaymentResponse);
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;
}