#java
#java
Вопрос:
Я создаю набор веб-сервисов с намерением агрегировать аналогичные наборы данных в нескольких серверных системах (посредством вызовов db и сервисных вызовов). Выполнение некоторых запросов может занять более пары секунд, и если я сложу эти запросы последовательно, есть вероятность, что общее время выполнения будет превышать желаемое время отклика.
Я надеюсь выполнять вызовы параллельно, собирать все результаты и затем агрегировать. Каков наилучший подход к решению этой проблемы?
Службы будут развернуты в Websphere 6.1 (таким образом, java 5, j2ee 1.4).
Любая информация была бы высоко оценена.
Ответ №1:
Загляните в java.util.concurrent API. В частности, вы можете создать исполнителя потока, передать в него вызываемые s и получить обратно будущиеs, которые будут выполняться асинхронно.
Ваш код будет выглядеть примерно так:
ExecutorService exec = Executors.newCachedThreadPool();
Future<ReplyA> raFuture = exec.submit(new Callable<ReplyA>() {
public ReplyA call() {
// call remote service here.
return new ReplyA(...);
}});
Future<ReplyB> rbFuture = exec.submit(new Callable<ReplyB>() {
public ReplyB call() {
// call remote service here.
return new ReplyB(...);
}});
ReplyA replyA = raFuture.get();
ReplyB replyB = rbFuture.get();
exec.shutdown();
Вы также можете использовать версии get() с таймаутом ожидания, чтобы вы могли сделать что-то разумное, если ответы занимают слишком много времени. Если вы решите пойти по этому пути, вам, вероятно, будет лучше использовать метод invokeAll от ExecutorService, поэтому время ожидания будет применяться ко всем вызываемым объектам как к группе:
Callable<Reply> taskA = new Callable<ReplyA>() { ... };
Callable<Reply> taskB = new Callable<ReplyB>() { ... };
List<Callable<Reply>> tasks = Arrays.asList(taskA, taskB);
List<Future<Reply>> futures = exec.invokeAll(tasks, 20, TimeUnit.SECONDS);
for(Future<Reply> future: futures) {
if(replyFuture.isCancelled()) {
// deal with it
} else {
Reply reply = future.get();
// do something with the reply.
}
}
Комментарии:
1. Если OP собирается реализовать сервисы как методы EJB, не вызовет ли такой подход проблемы? Согласно спецификациям, EJB не должны создавать собственные потоки (как контейнер справляется с этим, это другой вопрос… если контейнер вообще с этим справляется.)
2. я не планировал создавать их как EJBS. Я планировал создать серию объектов-значений, которые были бы доступны через веб-службы.
3. это решение привело меня к API WorkManager, который я буду использовать. С их помощью я могу включить в EJB, если захочу использовать EJBS.
Ответ №2:
Запустите потоки демонов, позвольте им выполнить работу, объедините () их все, агрегируйте.
Другой возможный способ — поместить запросы в очередь и позволить MDBS обрабатывать по одному запросу за раз. Однако агрегирование немного более запутанное — приходится получать результаты из другой очереди, накапливать их между ответами и обрабатывать ошибки… ну, обычное дело, просто выполняйте потоки! 🙂
RequestAThread rath = new RequestAThread(dataForRequestA);
RequestBThread rbth = new RequestBThread(dataForRequestB);
...
rath.start();
rbth.start();
...
rath.join();
ReplyA ra = rath.getReply();
rbth.join();
ReplyB rb = rbth.getReply();
Result r = aggregate(ra,rb);
Обработка ошибок по вкусу.
Комментарии:
1. есть какие-либо опасения по поводу потоков в контейнере приложения? кажется, я где-то читал, что это направление было менее чем желательным (сервер приложений ими не управляет).
2. Действительно, согласно спецификациям EE, EJB и другие управляемые объекты не должны создавать потоки самостоятельно.
3. и в случае, если я решу использовать EJBs, единственным вариантом будет MDB? Кроме того, сталкивались ли вы с какими-либо примерами онлайн для обработки этого с помощью EJBs? Я хотел бы подробнее изучить это.