【问题标题】:In the new ASP.NET Web API, how do I design for "Batch" requests?在新的 ASP.NET Web API 中,如何设计“批处理”请求?
【发布时间】:2012-03-02 07:23:38
【问题描述】:

我正在基于新的 ASP.NET Web API 创建一个 Web API。我试图了解处理同时提交多个数据集的人的最佳方式。如果他们有 100,000 个请求,最好让他们一次提交 1,000 个。

假设我的联系人控制器中有一个新的联系人方法:

public string Put(Contact _contact)
{
    //add new _contact to repository
    repository.Add(_contact);

    //return success
}

允许用户“批量”提交新联系人的正确方法是什么?我在想:

public string BatchPut(IEnumerable<Contact> _contacts)
{
    foreach (var contact in _contacts)
    {
        respository.Add(contact);
    }
}

这是一个好习惯吗?这是否会解析带有 JSON 联系人数组的 GET 请求(假设它们的格式正确)?

最后,关于如何最好地响应 Batch 请求的任何提示?如果 300 个中有 4 个失败了怎么办?

谢谢一百万!

【问题讨论】:

  • 在请求大小、用户数量、用户类型、数据质量、时间限制和数据库可靠性方面,您对服务器有什么保证?这些会影响您可用的设计选择吗?
  • 我有一个 API 可以为每个 API 请求创建 1 个作业。到目前为止,API 一次只接受 1 个作业。这个限制让需要发出多达 200,000 个请求的大客户感到头疼。每个单独的 API 调用都作为一条消息进入 Amazon SQS,因此并发性不是问题,只是让大量客户感到头疼。谢谢!
  • 您的业务流程是否允许异步处理?例如,当我在一家保险公司工作时,我们习惯于排队计算报价,因此客户提交数据并在创建报价时进行轮询。这需要一两到 30 分钟,具体取决于可用资源。
  • 是的异步...我进行授权检查,将请求格式化为 JSON 对象,然后将其传递给 Amazon 队列。单独的工作角色处理作业。
  • 我正在做类似的事情并更新了我的答案以反映我的策略。

标签: asp.net-mvc-4 asp.net-web-api


【解决方案1】:

当您 PUT 集合时,您要么插入整个集合,要么替换现有集合,就像它是单个资源一样。它与 GET、DELETE 或 POST 集合非常相似。这是一个原子操作。使用 is 代替单独调用 PUT 联系人可能不是非常 RESTfull(但这确实值得商榷)。

您可能想查看HTTP pipelining 并发送同一套接字的多个 PutContact 请求。对于每个请求,您都可以为该单个请求返回标准 HTTP 状态。

我过去使用 SOAP 实现了批量更新,当系统处于负载状态时,我们遇到了许多无法预料的问题。如果你不注意,我怀疑你会遇到同样的问题。

  1. 例如,数据库可能在批量更新过程中超时,并且在故障、可靠性、事务等方面彻底崩溃。可怜的客户端必须弄清楚实际更新了什么,然后再试一次。
  2. 当要更新的记录太多时,HTTP 请求会因为我们花费的时间太长而超时。这又打开了一罐蠕虫。
  3. 另一个问题是我们将在更新期间接受多少数据? 10MB 的联系人足够了吗?也许1MB?更大的缓冲区对内存使用和安全性有很多影响。

因此我建议查看HTTP pipelining

更新

我的建议是将批量创建联系人作为异步过程处理。只需假设“作业”与“批量创建”过程相同。所以服务可能如下所示:

public class JobService
{
    // Post
    public void Create(CreateJobRequest job)
    {
       // 1. Create job in the database with status "pending"
       // 2. Save job details to disk (or S3)
       // 3. Submit the job to MSMQ (or SQS)
       // 4. For 20 seconds, poll the database to see if the job completed
       // 5. If the job completed, return 201 with a URI to "Get" method below
       // 6. If not, return 202 (aka the request was accepted for processing, but has not completed)
    }

    // Get
    public Job Get(string id)
    {
       // 1. Fetch the job from the database
       // 2. Return the job if it exists or 404
    }
}

使用队列中的内容的后台进程可以更新数据库,或者对服务执行 PUT 以将 Job 的状态更新为正在运行和已完成。

您将需要另一个服务来浏览刚刚处理的数据、解决错误等等。

您的后台进程可能需要容忍验证错误。如果没有,或者您的服务进行了验证(假设您没有进行无法保证响应时间的数据库调用等),您可以返回一个类似 CreateJobResponse 的结构,其中包含足够的信息供您的客户解决问题并重新提交请求。如果您必须进行一些耗时的验证,请在后台进程中进行,将作业标记为失败并使用允许客户端修复错误并重新提交请求的信息更新作业。这假设客户端可以对作业失败的事实做一些事情。

如果 Create 方法将作业请求分解为许多较小的“作业”,您将不得不处理这样一个事实,即它可能不是原子的,并且对监控作业是否成功完成提出了许多挑战。

【讨论】:

  • 嗨,Werner,感谢您的建议。我不应该使用 PUT 动词——我的错误。我重新表述了上面的问题,如果你有时间再看一遍。干杯!
  • 这太棒了维尔纳,非常感谢!你在回答我还没想过要回答的问题。我仍然很好奇 MVC web api 中的模型绑定是否足够智能以处理如上所示的自定义对象列表。听起来我有更大的问题要处理。干杯!
【解决方案2】:

PUT 操作应该替换资源。通常,您对单个资源执行此操作,但在对集合执行此操作时,这意味着您将原始集合替换为传递的数据集。不确定您是否打算这样做,但我假设您只是更新集合的子集,在这种情况下,PATCH 方法会更合适。

最后,关于如何最好地响应 Batch 请求的任何提示?如果 300 个中有 4 个失败了怎么办?

这完全取决于您。只有一个响应,因此您可以发送 200 OK 或 400 Bad Request 并将详细信息放在正文中。

【讨论】:

  • 谢谢莫里斯-我不应该说 PUT。我的目标是让人们批量创建新联系人(比如一次 1000 个)。
猜你喜欢
  • 2014-03-26
  • 2014-10-06
  • 1970-01-01
  • 2023-02-09
  • 1970-01-01
  • 2014-10-06
  • 2010-09-10
  • 1970-01-01
  • 2016-09-26
相关资源
最近更新 更多