如何在多线程环境中实现有限的API调用?

时间:2017-02-14 09:52:00

标签: c# multithreading amazon-web-services rate-limiting

我有很多请求要发送到服务器(亚马逊产品广告API),但是这个服务器不允许我每秒发送超过1个请求。 我正在寻找一种基于C#技能的免费解决方案。

我试着像这样编码:

public static WebResponse Request(Dictionary<string, string> parameters) {
  var response = LaunchRequest(parameters);
  Sleep(1000);
  return response;
}

问题是多个线程正在进入方法&#34; Request&#34;同时。如果有4个线程,那么每秒将有4个请求。

我的线程如何相互等待?

编辑:我试了锁。

所以我写了这段代码:

public class RequestHandler {
  private static readonly RequestHandler instance = new RequestHandler();

  private Object thisLock = new Object();

  private RequestHandler() { }

  public static RequestHandler Instance {
    get {
      return instance;
    }
  }

  public WebResponse Request(Dictionary<string, string> parameters) {
    lock (thisLock) {
      Log();
      var response = LaunchRequest(parameters);
      Sleep(1000);
      return response;
    }
  }
}

所以我这样称呼我的方法:

// ...
RequestHandler requestHandler = RequestHandler.Instance;
WebResponse response = requestHandler.Request(requestHelper, parameters);
// ...

它似乎在大部分时间都有效,但有时候,我的&#34; LaunchRequest&#34;方法几乎同时被解雇。

编辑2: 以下是日志结果,箭头显示的是小于1秒的调用:

Request: 09:52:50.230  - Thread 22
Request: 09:52:48.830  - Thread 5
Request: 09:52:47.468  - Thread 10 <---
Request: 09:52:47.331  - Thread 13 <---
Request: 09:52:45.971  - Thread 12
Request: 09:52:44.767  - Thread 11
Request: 09:52:43.230  - Thread 5
Request: 09:52:30.546  - Thread 21 <--- 
Request: 09:52:30.357  - Thread 20 <---
Request: 09:52:29.232  - Thread 13
Request: 09:52:27.908  - Thread 11
Request: 09:52:26.471  - Thread 5
Request: 09:52:25.138  - Thread 11
Request: 09:52:23.835  - Thread 12

2 个答案:

答案 0 :(得分:2)

创建SQS队列。 在控制器中,将数据放入队列。 创建一个.net服务,该服务将逐个轮询队列并处理消息,确保在任何给定时间只发送一条消息。

UPD 如果您不喜欢AWS服务,不确定为什么要将帖子标记为AWS,但仍然如此。

您可以使用任何可用的不同队列,而不是SQS,例如MSMQ,RabbitMQ等。成本需要自己安装和维护。

绝对最糟糕的情况,使用C#中的ConcurrentQueue类型,并运行将从该队列弹出的后台线程。这将确保一次只处理一条消息。但是,此解决方案不具备弹性,因为应用程序池回收会擦除您的数据。

答案 1 :(得分:2)

将最后的通话详细信息存储在会话或本地变量中。如果有电话,你可以打电话给睡眠。

public static WebResponse Request(Dictionary<string, string> parameters) 
{
    lock (thisLock)
    {
        string lastCalled = Session["LastCalledTime"] as string;
        if (!string.IsNullOrEmpty(lastCalled) && DateTime.Parse(lastCalled) >= DateTime.Now.AddSeconds(-1))
        {
            Sleep(1000);
        }
        var response = LaunchRequest(parameters);
        Session["LastCalledTime"] = DateTime.Now.ToString("O");
    }

    return response;
 }

添加锁以使其线程安全,如trailmax所示。