限时服务

时间:2009-04-24 01:35:03

标签: java web-services concurrency queue policy

我正在开发一个向Musicbrainz网络服务请求的应用程序。我在musicbrainz手册中读到,每秒不向Web服务发出多个请求,否则客户端IP将被阻止。

为了使此限制对服务客户端透明,您建议使用什么体系结构?

  • 我想调用一个方法(例如getAlbuns),它应该只在最后一个请求后1秒发出请求。
  • 我也想一次调用10个请求,服务应该处理排队,并在可用时返回结果(非阻塞)。

谢谢!

2 个答案:

答案 0 :(得分:1)

您需要定义本地客户将呼叫的本地“代理服务”。

本地代理将接收请求并将其传递给实际服务。但仅限于每秒一条消息的速率。

你如何做到这一点在很大程度上取决于你可以使用的技术。

最简单的是一个带有静态和同步的LastRequestTime long的mutithreaded java服务;“timestamp变量。(虽然你需要一些代码杂技来保持你的请求顺序)。

更复杂的服务可以让工作线程接收请求并将它们放在队列中,单个线程接收请求并将它们传递给实际服务。

答案 1 :(得分:1)

由于调用之间需要延迟,我建议使用java.util.Timerjava.util.concurrent.ScheduledThreadPoolExecutorTimer非常简单,非常适合用例。但是,如果稍后确定其他计划要求,则单个Executor可以处理所有这些要求。在任何一种情况下,都使用固定延迟方法,而不是固定速率方法。

重复任务polls请求对象的并发队列。如果有待处理的请求,则任务执行该请求,并通过回调返回结果。服务的查询和要调用的回调是请求对象的成员。

应用程序保留对共享队列的引用。要安排请求,只需将其添加到队列中即可。


只是为了澄清,如果在执行计划任务时队列为空,则不会发出任何请求。简单的方法就是结束任务,调度程序将在一秒后调用任务再次检查。

但是,这意味着即使最近没有处理任何请求,也可能需要一秒钟才能启动任务。如果这种不必要的延迟是无法容忍的,那么编写自己的线程可能比使用TimerScheduledThreadPoolExecutor更可取。在您自己的定时循环中,如果您选择阻止空队列直到请求可用,则可以更好地控制调度。在上次执行完成之后,内置计时器无法保证等待一整秒;它们通常相对于任务的开始时间安排。

如果第二种情况是您的想法,那么您的run()方法将包含一个循环。每次迭代在队列上以blocking开始,直到收到请求,然后记录时间。处理完请求后,再次检查时间。如果时差小于一秒,则余数为sleep。此设置假定在一个请求的开始和下一个请求之间需要一秒的延迟。如果在一个请求结束和下一个请求之间需要延迟,则无需检查时间;只是睡了一秒钟。

还有一点需要注意的是,该服务可能能够在单个请求中接受多个查询,从而减少开销。如果是这样,请通过阻塞take()第一个元素,然后使用poll(),可能使用非常短的阻塞时间(大约5毫秒)来查看应用程序是否正在制作任何更多的要求。如果是这样,可以将这些捆绑在服务的单个请求中。如果queueBlockingQueue<? extends Request>,则可能如下所示:

    Collection<Request> bundle = new ArrayList<Request>();
    bundle.add(queue.take());
    while (bundle.size() < BUNDLE_MAX) {
      Request req = queue.poll(EXTRA, TimeUnit.MILLISECONDS);
      if (req == null)
        break;
      bundle.add(req);
    }
    /* Now make one service request with contents of "bundle". */