Ограниченное по времени обслуживание - PullRequest
1 голос
/ 24 апреля 2009

Я занимаюсь разработкой приложения, которое отправляет запросы в веб-службу Musicbrainz. Я прочитал в руководстве musicbrainz, чтобы не делать больше одного запроса в секунду к веб-сервису, иначе IP-адрес клиента будет заблокирован.

Какую архитектуру вы предлагаете, чтобы сделать это ограничение прозрачным для клиента службы.

  • Я хотел бы вызвать метод (например, getAlbuns), и он должен сделать запрос только через 1сек после последнего запроса.
  • Я также хочу вызвать 10 запросов одновременно, и служба должна обработать очередь, возвращая результаты, когда они доступны (неблокирование).

Спасибо!

Ответы [ 2 ]

1 голос
/ 24 апреля 2009

Из-за необходимой задержки между вызовами, я бы предложил java.util.Timer или java.util.concurrent.ScheduledThreadPoolExecutor. Timer очень просто и идеально подходит для этого варианта использования. Но если дополнительные требования к планированию будут определены позже, один Executor может обработать все из них. В любом случае используйте метод с фиксированной задержкой, а не метод с фиксированной скоростью.

Повторяющаяся задача опрашивает параллельную очередь для объекта запроса. Если есть ожидающий запрос, задача выполняет его и возвращает результат посредством обратного вызова. Запрос на службу и обратный вызов для вызова являются членами объекта запроса.

Приложение хранит ссылку на общую очередь. Чтобы запланировать запрос, просто добавьте его в очередь.


Просто чтобы уточнить, если очередь пуста при выполнении запланированной задачи, запрос не выполняется. Простой подход состоит в том, чтобы просто завершить задачу, и планировщик вызовет задачу через секунду, чтобы проверить снова.

Однако это означает, что запуск задачи может занять до одной секунды, даже если в последнее время запросы не обрабатывались. Если эта ненужная задержка недопустима, вероятно, предпочтительнее написать собственный поток, чем использовать Timer или ScheduledThreadPoolExecutor. В вашем собственном цикле синхронизации у вас есть больший контроль над расписанием, если вы решите блокировать пустую очередь до тех пор, пока не будет доступен запрос. Встроенные таймеры не гарантированно ждут целую секунду после предыдущего выполнения Закончено ; они обычно планируются относительно времени начала задания.

Если вы имеете в виду этот второй случай, ваш метод run() будет содержать цикл. Каждая итерация начинается с , блокируя в очереди до получения запроса, затем записывая время. После обработки запроса время проверяется снова. Если разница во времени составляет менее одной секунды, 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". */
1 голос
/ 24 апреля 2009

Вам необходимо определить локальную «прокси-службу», которую будут вызывать ваши локальные клиенты.

Локальный прокси-сервер будет получать запросы и передавать их в реальный сервис. Но только со скоростью одного сообщения в секунду.

То, как вы это сделаете, очень сильно зависит от доступной вам технологии.

Самым простым будет многопоточный java-сервис со статической и синхронизированной переменной LastRequestTime long; "timestamp" (хотя вам потребуется некоторая акробатика кода, чтобы ваши запросы были в последовательности).

Более сложный сервис мог бы иметь рабочие потоки, принимающие запросы и помещающие их в очередь, а один поток собирал запросы и передавал их в реальный сервис.

...