【问题标题】:Concurrent request in ASP.NET Web API 2 is nearly six times slower than a single requestASP.NET Web API 2 中的并发请求比单个请求慢近六倍
【发布时间】:2018-08-27 15:00:53
【问题描述】:

背景:

我们构建了一个与 ASP.NET Web API 2 后端通信的 React SPA,.NET Framework 4.6.1。在我们的初始加载中,我们发出两个单独的请求来加载数据。在加载大量数据时,我们注意到在应用程序中发出 API 请求时,它们都比在 Postman 中单独尝试请求时慢得多。

原文:

示例结构,fetch当然使用我们的API:

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(JSON.stringify(myJson));
  });

fetch('http://example.com/otherMovies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(JSON.stringify(myJson));
  });

示例 C# API 方法:

[HttpGet]
[Route("{bsid}/{caseId}")]
public IHttpActionResult GetRenewalCycleForCustomer(string bsid, int caseId)
{
    var customerNumbers = GetCustomerNumbers();

    var userId = HttpContext.Current.User.Identity.GetUserId<int>();

    var user = identityDb.Users.Find(userId);

    var customerNumbers = user.ApplicationUserCustomerNumbers.Select(x => new CustomerNumberKey() { bsid = x.CustomerNumber.bsid, NameNo = x.CustomerNumber.NameNo }).ToList();

    var db = new DbContext();

    var caseService = new CaseService(db);

    var portfolioTabViewModel = caseService.GetPortfolioTabViewModelForCustomer(bsid, caseId, customerNumbers);

    return Ok(portfolioTabViewModel);
}

操作系统是 Windows 10 Pro,IIS 应该能够根据 Internet 处理 10 个并发连接。无论如何都没有关系,因为我们将它作为 Windows Server 托管在 Azure 上,并且在那里我们有相同的响应时间。也试过应用服务,结果是一样的。

使用 Postman Runner 同步测试响应时间

两个 Postman Runner 实例同时运行:

其他说明:

似乎与硬件无关,因为无论是否同时发出请求,CPU、内存和磁盘都不会受到影响。

我们可以做些什么来解决这个问题?

似乎并行运行的非异步方法:

https://stackoverflow.com/a/26737847/3850405

一些线程建议会话状态,但我在Web.config 中找不到任何引用,这不是我启用的任何内容。搜索 HttpContext.Current.SetSessionStateBehavior 得到 0 个结果。

https://stackoverflow.com/a/26172317/3850405

Windows 10 资源:

https://serverfault.com/a/800518/293367

https://forums.asp.net/t/2100558.aspx?concurrent+connections+on+windows+pro+10+IIS

更新异步和 IIS:

好像和async或者IIS没有关系,用下面的方法测试并发请求。并发的慢请求似乎取决于其他东西。

异步:

[HttpGet]
[Route("{bsid}/{caseId}")]
public async Task<IHttpActionResult> Get(string bsid, int caseId)
{
    await Task.Delay(3000);
    return Ok();
}

同步:

[HttpGet]
[Route("{bsid}/{caseId}")]
public IHttpActionResult Get(string bsid, int caseId)
{
    Thread.Sleep(3000);
    return Ok();
}

更新 2:具有异步和同步调用的数据库:

似乎也不是数据库调用。使用Include 进行测试,调用的速度相似,尽管异步调用要慢得多。

异步:

[HttpGet]
[Route("{bsid}/{caseId}/async")]
public async Task<IHttpActionResult> GetAsync(string bsid, int caseId)
{
    var db = new DbContext();

    var deviations = await db.RenewalCycles.Where(x => x.Deviations.Any())
        .Include(cycle => cycle.TPCase.CaseNames.Select(caseName => caseName.TPName))
        .Include(cycle => cycle.TPCase.CaseNames.Select(caseName => caseName.TPNameType))
        .Include(cycle => cycle.TPCase.GoodsAndServicesDescriptions)
        .Include(cycle => cycle.TPCase.RelatedCases.Select(relatedCase => relatedCase.TPCaseRelation))
        .Include(cycle => cycle.TPCase.RelatedCases.Select(relatedCase => relatedCase.TPCountry))
        .ToListAsync();

    return Ok();
}

同步:

[HttpGet]
[Route("{bsid}/{caseId}")]
public IHttpActionResult Get(string bsid, int caseId)
{
    var db = new DbContext();

   var deviations = db.RenewalCycles.Where(x => x.Deviations.Any())
        .Include(cycle => cycle.TPCase.CaseNames.Select(caseName => caseName.TPName))
        .Include(cycle => cycle.TPCase.CaseNames.Select(caseName => caseName.TPNameType))
        .Include(cycle => cycle.TPCase.GoodsAndServicesDescriptions)
        .Include(cycle => cycle.TPCase.RelatedCases.Select(relatedCase => relatedCase.TPCaseRelation))
        .Include(cycle => cycle.TPCase.RelatedCases.Select(relatedCase => relatedCase.TPCountry))
        .ToList();

    return Ok();
}

【问题讨论】:

标签: c# asp.net iis concurrency iis-10


【解决方案1】:

毕竟,市长开销部分与数据库有关。一种方法使用db.Cases.Find(bsid, caseId),然后将模型转换为视图模型。 Cases 模型反过来有很多关系,所有这些都发出单独的数据库调用,因为该模型具有标记为 virtual 的属性,例如 public virtual TPRenewalCycle TPRenewalCycle { get; set; } 以启用延迟加载。通过查看 Visual Studio 输出窗口(调试 -> Windows -> 输出)并设置 ApplicationDbContext 找到它,如下所示。

public class ApplicationDbContext : DbContext
{
    protected const string ConnectionStringName = "defaultConnection";
    public ApplicationDbContext() : base(ConnectionStringName)
    {
        Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
        Database.CommandTimeout = 300;
    }

【讨论】:

    猜你喜欢
    • 2023-04-05
    • 2016-06-23
    • 2015-09-21
    • 2017-10-15
    • 2016-05-12
    • 1970-01-01
    • 2015-04-09
    • 2014-09-02
    • 2020-07-06
    相关资源
    最近更新 更多