Транзакционный маршрут и транзакционные конечные точки, порядок фиксации транзакции - PullRequest
0 голосов
/ 06 марта 2020

Мой маршрут выглядит следующим образом

from("jms:queue:IN_QUEUE) //(A) Transactional Endpoint .transacted("required") //(B) TX Policy with PROPAGATION_REQUIRED and JPATxManager .bean("someBean", "readFromDB()") //(C) Read from DB .bean("someBean", "writeToDB()") //(D) Write to DB .to("file:/home/src?fileName=demo_${id}.txt")

Я знаю, что потребитель JMS в (A) будет отбрасывать транзакцию JMS при каждом опросе и подключаться к потоку. Кроме того, транзакционный узел в (B) будет разветвлять транзакцию JPA после того, как обмен достигнет ее и присоединится к потоку.

Пожалуйста, найдите мои вопросы ниже:

  1. Могут ли две разные транзакции получить присоединен к одному потоку (как вышеупомянутый)?
  2. Если да, какой из них должен быть приостановлен?
  3. Каким должен быть порядок фиксации и отката вышеупомянутого маршрута?

    Примечание: я не нашел никакого очевидного ответа от книги «Верблюд в действии», 2-е издание, поэтому, пожалуйста, наставьте меня

Ответы [ 2 ]

1 голос
/ 10 марта 2020

Добрый день,

Это вариант вашего другого вопроса.

Конечная точка:

from("jms:queue:IN_QUEUE)      //(A) Transactional Endpoint

передана, то есть вы пометили компонент JMS после выполнения транзакции, а JMS-сеансы будут управляться JmsTransactionManager.

.transacted("required")   //(B) TX Policy with PROPAGATION_REQUIRED and 
JPATxManager

Это должен быть не менеджер транзакций JPA, а менеджер транзакций JTA (например, Arjuna). Как и в вашем другом вопросе, теперь у вас есть локальная транзакция JMS для чтения вашего сообщения и локальные транзакции JPA для доступа к вашей БД. Вы хотите, чтобы PlatformTransactionManager (менеджер транзакций JTA) синхронизировал для вас локальные транзакции.

Что касается ваших вопросов:

Могут ли две разные транзакции быть присоединены к одному потоку (например, как указано выше) )?

Это действительно не имеет никакого смысла.

Если Да, какой из них должен быть приостановлен?

Ничто не будет приостановлено.

Каким должен быть порядок фиксации и отката вышеупомянутого маршрута?

Чтение БД не является транзакционным и не требует фиксации. Запись в файл на самом деле произойдет, когда транзакционный контекст JTA будет закрыт. Это оставляет БД записи. Если это не удастся, то чтение БД не имеет значения, сообщение будет возвращено в место назначения источника и запись файла не будет вызвана.

Включение ведения журнала DEBUG для различных менеджеров транзакций очень полезно.

Я мог бы go об этом с этой болезненной деталью. Это больше для бурки. Я думаю, что вы действительно оцените это. Очень тонко, и это часто случается.

   from("jms:queue:SRC_QUEUE")
     .transacted("required")
     .to("jms1:queue:DEST_QUEUE") 

Что произойдет, если две конечные точки помечены как транзакционные ... но ... у вас нет строки "транзакционная"? Хорошо, локальная транзакция JMS была запущена на приемнике сообщений. Это будет совершено по окончании маршрута. Существует две независимые локальные транзакции JMS. Они не синхронизируются менеджером транзакций JTA.

На самом деле происходит то, что вызывается коммит для сообщения 'get'. Для сообщения 'put' нет фактической фиксации. Сообщение «положить» фиксируется, когда сеанс JMS закрыт. Это в JMS spe c, которое закрывает соединение по своей природе и фиксирует любую транзакцию. Таким образом, поскольку между двумя компонентами нет никакой связи, «get» фиксируется, и затем сеанс «put» закрывается.

Это означает, что вы можете потерять сообщения, если происходит сбой между фиксацией для сообщения 'get' и закрытием сеанса для сообщения 'put'.

Имеет ли это смысл? Нет связи между локальными транзакциями, поэтому Camel закрывает их по порядку, начиная с фиксации get до вызова put.

Ключом является синхронизация транзакций JTA. У вас все еще есть ресурсы локальных транзакций (не XA), но ими можно очень хорошо управлять в довольно легком транзакционном контексте JTA.

   from("jms:queue:SRC_QUEUE")
     .transacted("required")
     .to("DB:transactedwrite")
     .to("jms1:queue:DEST_QUEUE") 

Мне не удавалось найти правильный синтаксис для базы данных. вставить, но вы поняли. В этом случае вы можете получить дубликаты вставок в БД, если JMS 'put' завершится неудачно. Это не «все или ничего» транзакции XA. Сделки совершаются в порядке. Если кто-то посередине преуспевает, то следующая транзакция терпит неудачу, хорошо, 'get' будет откатываться, и вы будете получать дубликаты вплоть до точки сбоя.

0 голосов
/ 09 марта 2020

Извините, я не могу ответить на ваши конкретные c вопросы, но могу дать некоторую конкретную c информацию о транзакциях вашего маршрута.

У вас есть 3 разных "системы" с различные области действия транзакций

  • JMS-брокер, из которого вы потребляете
  • База данных, из которой вы читаете и пишете, и вы сконфигурировали JPA TxManager для
  • Файловая система (без транзакций) в качестве пункта назначения

Прежде всего, , если вы хотите обеспечить безопасность транзакций в JMS и базе данных, вам нужно использовать транзакции XA .

Тогда неясно, если вы ожидаете потребителя JMS для транзакции (из-за transacted() в вашем маршруте) или если вы действительно настроили JMS связь с локальными транзакциями JMS. Я предполагаю, что вы действительно потребляете транзакции.

Давайте поговорим о том, что вы получили без строки B вашего маршрута:

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

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

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

Таким образом, чтобы решить эти проблемы, вы должны либо использовать транзакции XA, либо просто использовать локальные транзакции JMS и реализовать логи компенсации c для «пробелов», подобных описанному выше.

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

...