我正在开发一个向Musicbrainz网络服务请求的应用程序。我在musicbrainz手册中读到,每秒不向Web服务发出多个请求,否则客户端IP将被阻止。
为了使此限制对服务客户端透明,您建议使用什么体系结构?
谢谢!
答案 0 :(得分:1)
您需要定义本地客户将呼叫的本地“代理服务”。
本地代理将接收请求并将其传递给实际服务。但仅限于每秒一条消息的速率。
你如何做到这一点在很大程度上取决于你可以使用的技术。
最简单的是一个带有静态和同步的LastRequestTime long的mutithreaded java服务;“timestamp变量。(虽然你需要一些代码杂技来保持你的请求顺序)。
更复杂的服务可以让工作线程接收请求并将它们放在队列中,单个线程接收请求并将它们传递给实际服务。
答案 1 :(得分:1)
由于调用之间需要延迟,我建议使用java.util.Timer
或java.util.concurrent.ScheduledThreadPoolExecutor
。 Timer
非常简单,非常适合此用例。但是,如果稍后确定其他计划要求,则单个Executor
可以处理所有这些要求。在任何一种情况下,都使用固定延迟方法,而不是固定速率方法。
重复任务polls请求对象的并发队列。如果有待处理的请求,则任务执行该请求,并通过回调返回结果。服务的查询和要调用的回调是请求对象的成员。
应用程序保留对共享队列的引用。要安排请求,只需将其添加到队列中即可。
只是为了澄清,如果在执行计划任务时队列为空,则不会发出任何请求。简单的方法就是结束任务,调度程序将在一秒后调用任务再次检查。
但是,这意味着即使最近没有处理任何请求,也可能需要一秒钟才能启动任务。如果这种不必要的延迟是无法容忍的,那么编写自己的线程可能比使用Timer
或ScheduledThreadPoolExecutor
更可取。在您自己的定时循环中,如果您选择阻止空队列直到请求可用,则可以更好地控制调度。在上次执行完成之后,内置计时器无法保证等待一整秒;它们通常相对于任务的开始时间安排。
如果第二种情况是您的想法,那么您的run()
方法将包含一个循环。每次迭代在队列上以blocking开始,直到收到请求,然后记录时间。处理完请求后,再次检查时间。如果时差小于一秒,则余数为sleep。此设置假定在一个请求的开始和下一个请求之间需要一秒的延迟。如果在一个请求结束和下一个请求之间需要延迟,则无需检查时间;只是睡了一秒钟。
还有一点需要注意的是,该服务可能能够在单个请求中接受多个查询,从而减少开销。如果是这样,请通过阻塞take()
第一个元素,然后使用poll()
,可能使用非常短的阻塞时间(大约5毫秒)来查看应用程序是否正在制作任何更多的要求。如果是这样,可以将这些捆绑在服务的单个请求中。如果queue
是BlockingQueue<? 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". */