Мне нужны две службы REST, которые являются частью одной транзакции (распределенной транзакции), поэтому, если, например, клиент вызывает SERVICE_1, который был успешным, а затем SERVICE_2, который не был успешным, SERVICE_2 И SERVICE_1 должен выполнить откат.
Я успешно добился этого с помощью диспетчера транзакций Atomikos (TransactionEssentials), служб, реализованных с помощью Apache CXF, и клиента Java (оркестратор транзакций), который вызывает службы REST с помощью JAX-RS. Я основывал свою работу на отличных примерах, предоставленных Atomikos, которые можно найти здесь: https://www.atomikos.com/downloads/extreme-transactions/com/atomikos/examples/5.0.6/examples-5.0.6-project.zip (примеры-jta-rest-jaxrs).
Проблема, с которой я столкнулся, заключается в том, что когда я заменяю реализацию служб REST на Spring Boot, глобальная транзакция больше не работает. Я сузил проблему до службы Spring Boot REST, которая не возвращает атрибут Atomikos-Extent в заголовке, который требуется вызывающей стороне (которая начинает и фиксирует глобальную транзакцию). Это исключение, которое вызывает вызывающий абонент:
WARNING: Invalid extent found - any remote work will time out and rollback.
java.lang.IllegalArgumentException: Expected an extent but found none. The remote work will not be committed by us.
at com.atomikos.remoting.DefaultExportingTransactionManager.addExtent(DefaultExportingTransactionManager.java:70)
Итак, вопрос в том, как реализовать службу Spring Boot REST, чтобы она могла участвовать в глобальных транзакциях с помощью диспетчера транзакций Atomikos.
Ниже приведены некоторые важные фрагменты кода.
Клиент / звонящий
Client client1= newClient().register(JacksonJsonProvider.class)
.register(ParticipantsProvider.class)
.register(TransactionAwareRestClientFilter.class);
Client client2 = //same as client1
UserTransactionManager utm = new UserTransactionManager();
utm.init();
utm.begin();
callService1();
callService2();
utm.commit();
utm.close();
Услуга
@EnableTransactionManagement
@SpringBootApplication
public class SpringBootAtomikosExampleApplication {
//..
@Bean(initMethod="init", destroyMethod="close")
public AtomikosDataSourceBean dataSource() {
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
//..
return ds;
}
}
@Transactional
@RestController
public class MyController {
@RequestMapping("/test")
public void test() throws Exception {
TransactionManager tm = new UserTransactionManager();
tm.begin();
try (Connection con = dataSource.getConnection();
Statement s = con.createStatement();) {
s.executeQuery("select count(*) from menu");
}
tm.commit();
}
}
Я нашел расплывчатую информацию о том, что при весенней загрузке вы должны использовать Atomikos ExtremeTransaction вместо TransactionEssentials, но я не уверен, что это решение.