У меня есть верблюжий маршрут, который будет обновлять данный идентификатор.

Для одного запроса все работает нормально, скажем: Если у меня есть несколько запросов с одинаковым идентификатором, скажем 10, тогда я хочу, чтобы один запрос выполнялся за раз .

Или сделать ожидание другого запроса с тем же идентификатором, тем временем, если у меня есть запрос с другим идентификатором, который можно обработать, не дожидаясь, скажем, 12.

Для этого я создал дополнительный маршрут в моем контексте Camel «addRequestToPool», который направит весь запрос, приходящий на «updateTicket» верблюда, в одно место, и оттуда должна выполняться обработка 1 на 1.

Я хочу реализовать что-то вроде ниже :

Когда запрос приходит к методу synchronizeRequests, он должен проверить, существует ли уже существующее задание с docketId, если есть, заставить его дождаться завершения старого процесса (скажем, 10). если такого идентификатора нет (скажем, 12), продолжить обработку.

После завершения старого процесса он должен сообщить другим заданиям с тем же идентификатором, ожидающим, что 1 из них может возобновить работу с того места, где они ожидали, чтобы ответ был отправлен.

Сбор запросов выполняется по маршруту «addRequestToPool». Как только запрос приходит, он создает поток для каждого запроса и вызывает метод синхронизации. Внутри метода синхронизации «RequestSynchronization» я хочу выполнить операцию проверки для существующего задания с идентификатором, если он существует, дождаться уведомления от предыдущего ответа, если он не вернет, и продолжить обработку.

Как только задание будет завершено в конце с использованием маршрута «removeRequestFromPool», текущий идентификатор задания будет удален, и новое задание в группе (с таким же идентификатором) должно быть уведомлено о продолжении.

Я хотел бы знать, как мне реализовать описанный выше сценарий или любой другой подходящий подход, если таковой имеется?

Ниже приведен образец кода для справки,

Верблюжий маршрут:

from("direct:updateTicket")
    .to("direct:addRequestToPool")
    .to("direct:getDetailForTicket")
    .to("direct:updateDetailForTroubleTicket")
    .to("direct:getDetailForTicket")
    .to("direct:removeRequestFromPool")
    .to("direct:endRoute");

Вызов API:

public class TicketRequestManagement {

    public SimpleActionResponse addRequestToPool(int docketNo) throws InterruptedException {

        SimpleActionResponse response = new SimpleActionResponse();

        RequestSynchronization requestSynchronization = new RequestSynchronization(docketNo);
        Thread thread = new Thread(requestSynchronization);
        thread.start();
        thread.join();
        return response;
    }
}

Метод синхронизации:

public class RequestSynchronization implements Runnable {

    private static Logger logger = Logger.getLogger(RequestSynchronization.class);

    private int docketNo;

    public RequestSynchronization(int docketNo) {
        super();
        this.docketNo = docketNo;
    }

    @Override
    public void run() {
        synchronizeRequests(docketNo);
    }

    public synchronized static void synchronizeRequests(int docketNo) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Я думал добавить docketId в статическую структуру данных (Set) и проверить, существует ли ожидание Id (как мне выполнить это ожидание?), Как только процесс будет завершен, удалите идентификатор из набора и уведомите процесс ожидания для возобновления оттуда (как я могу выполнить это уведомление с тем же идентификатором?).

Дайте мне знать, если потребуется какая-то ясность.

PS: Я не ищу сна, это просто для выполнения теста синхронизации.

Заранее спасибо.

0
Hargun Suri 7 Ноя 2019 в 12:38
Я не знаком с apache-camel , но, тем не менее, хотел рассказать вам об утилитах параллелизма JDK. См. Пакет java.util.concurrent, а также техническое руководство и, наконец, руководство. Конечно, это не единственные доступные ссылки. Google для утилит параллелизма java .
 – 
Abra
7 Ноя 2019 в 12:52

2 ответа

Мое предложение: переходите на асинхронный режим . Используйте, например, JMS (не напрямую, а с отличной поддержкой JMS для верблюдов) для разделения запросов и их обработки . Таким образом, вы можете контролировать всю работу с потоками, просто подсчитывая количество потребителей.

В вашем случае это будет

  • addRequestToPool помещает запрос в очередь JMS
  • потребитель JMS потребляет запросы из очереди и обрабатывает их
  • Используйте группы сообщений для синхронизации запросов с тем же идентификатором.
  • Используйте счетчик потребителей , чтобы обеспечить параллельную обработку других идентификаторов и масштабирование (вы пишете 0 строк кода для потоковой передачи)
  • Клиенты по-прежнему могут отправлять запросы на обновление, даже если компоненты обработки не работают на техническое обслуживание.

Группы сообщений - это функция брокера JMS (по крайней мере, ActiveMQ). Вы устанавливаете свой идентификатор как JMSXGroupID, и брокер следит за тем, чтобы все сообщения с одинаковым идентификатором обрабатывались одним и тем же потребителем . Если у вас 5 потребителей и вы получаете 10 сообщений с одним и тем же идентификатором, всю работу выполняет 1 потребитель. Как только приходит сообщение с другим идентификатором, другой потребитель обрабатывает его параллельно.

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

1
burki 11 Ноя 2019 в 11:16

Прежде всего, не запускайте Thread вручную, это то, для чего нужны ExecutorService.

Во-вторых, я бы посоветовал вам сохранить запущенные синхронизации в Map<Integer, Future<>>, чтобы вы могли проверить, выполняется ли что-то уже для этого идентификатора.

Черновой вариант был бы

class TicketRequestService {
    private Map<Integer, Future<?>> runningRequests = new ConcurrentHashMap<>();
    ExecutorService executor = Executors.newCachedThreadPool();

    public void synchronizeRequest(int docketId) {
        // get the running Future if present, otherwise...
        Future<?> future = runningRequests.computeIfAbsent(docketId,
            // ...create a new one
            id-> executor.submit(() -> {
                RequestSynchronization.synchronizeRequests(id));
                // remove future from map after sync is finished
                runningRequests.remove(docketId);
            });
        future.get(); // wait until finished; this throws Exceptions, you need to handle them
        return new SimpleActionResponse();
   }
}
-1
daniu 7 Ноя 2019 в 13:01