Java Spring集成:持久性和事务性队列通道
在Spring Integration中,我们有一个设置,如下所示:Java Spring集成:持久性和事务性队列通道,java,spring,spring-integration,Java,Spring,Spring Integration,在Spring Integration中,我们有一个设置,如下所示: ---> ---> (dispatcher) Messages --> Gateway ----> QueueChannel ---> MessageHandler (worker)
--->
--->
(dispatcher) Messages --> Gateway ----> QueueChannel ---> MessageHandler (worker)
--->
--->
因此,我们有一个调度程序线程,它从MQTT代理接收消息并将它们转发到队列中。队列的轮询器提供了TaskExecuter,因此使用者是多线程的。
我们设法实现了所有的功能。因此,刚才描述的设置已经实现
现在为了保证不丢失数据我们想做两件事:
1.:
我们希望队列能够持久化数据,因此当程序意外关闭时,队列中的所有数据仍然存在。
这也对我们有用,我们使用MongoDB作为数据库,因为我们在您的文档中读到,这是推荐的方法
2.:
我们要确保的第二件事是工作线程是事务性的。因此,只有当工作线程正确返回时,消息才会从队列中被永久删除(因此也将从持久消息存储中删除)。如果程序在处理消息期间(由工作线程)关闭,则消息在下次启动时仍将在队列中。
此外,例如,如果工作进程在消息处理过程中出现异常,它将被放回队列
我们的实施:
如前所述,程序的基本设置已经实现。然后,我们使用队列的消息存储实现扩展了基本实现
队列通道:
由Messagestore支持:
匹配的轮询器:
执行人:
我们尝试了什么?
正如现在所解释的,我们希望用事务功能扩展这个实现。我们尝试使用setTransactionSynchronizationFactory,如解释的那样,但它不起作用(没有出现错误或其他任何情况,但行为仍与添加TransactionSynchronizer之前一样):
在spring集成中实现我们的需求的最佳方式是什么
编辑:
根据答案中的建议,我选择使用JdbcChannelMessageStore执行此操作。因此,我尝试将上述XML实现(18.4.2)转换为Java。我不太确定该怎么做,这是我迄今为止尝试过的:
我创建了H2数据库并运行上面显示的脚本
已创建JDBCChannelMessageStore Bean:
@Bean
public JdbcChannelMessageStore store() {
JdbcChannelMessageStore ms = new JdbcChannelMessageStore();
ms.setChannelMessageStoreQueryProvider(queryProvider());
ms.setUsingIdCache(true);
ms.setDataSource(dataSource);
return ms;
}
已创建一个ChannelMessageStoreQueryProvider
@Bean
public ChannelMessageStoreQueryProvider queryProvider() {
return new H2ChannelMessageStoreQueryProvider();
}
调整民意测验:
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller() throws Exception {
PollerMetadata poll = Pollers.fixedDelay(10).get();
poll.setTaskExecutor(consumer);
poll.setAdviceChain(Collections.singletonList(transactionInterceptor()));
return poll;
}
自动连线我的PlaatformTransactionManager:
@Autowired
PlatformTransactionManager transactionManager;
@Bean
public TransactionInterceptor transactionInterceptor() {
return new TransactionInterceptorBuilder(true)
.transactionManager(transactionManager)
.isolation(Isolation.READ_COMMITTED)
.propagation(Propagation.REQUIRED)
.build();
}
并从TransactionManager创建TransactionInterceptor:
@Autowired
PlatformTransactionManager transactionManager;
@Bean
public TransactionInterceptor transactionInterceptor() {
return new TransactionInterceptorBuilder(true)
.transactionManager(transactionManager)
.isolation(Isolation.READ_COMMITTED)
.propagation(Propagation.REQUIRED)
.build();
}
如果您需要将队列作为事务性队列,那么您肯定应该查看事务性
MessageStore
。只有JDBC一个是这样的。只是因为只有JDBC支持事务。因此,当我们执行删除
时,只有在发送已提交时才可以
MongoDB或任何其他NoSQL数据库都支持这种模型,因此您只能在回滚时使用TransactionSynchronizationFactory
将失败的消息推送到DB
更新
@RunWith(SpringRunner.class)
@DirtiesContext
public class So47264688Tests {
private static final String MESSAGE_GROUP = "transactionalQueueChannel";
private static EmbeddedDatabase dataSource;
@BeforeClass
public static void init() {
dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:/org/springframework/integration/jdbc/schema-drop-h2.sql")
.addScript("classpath:/org/springframework/integration/jdbc/schema-h2.sql")
.build();
}
@AfterClass
public static void destroy() {
dataSource.shutdown();
}
@Autowired
private PollableChannel transactionalQueueChannel;
@Autowired
private JdbcChannelMessageStore jdbcChannelMessageStore;
@Autowired
private PollingConsumer serviceActivatorEndpoint;
@Autowired
private CountDownLatch exceptionLatch;
@Test
public void testTransactionalQueueChannel() throws InterruptedException {
GenericMessage<String> message = new GenericMessage<>("foo");
this.transactionalQueueChannel.send(message);
assertTrue(this.exceptionLatch.await(10, TimeUnit.SECONDS));
this.serviceActivatorEndpoint.stop();
assertEquals(1, this.jdbcChannelMessageStore.messageGroupSize(MESSAGE_GROUP));
Message<?> messageFromStore = this.jdbcChannelMessageStore.pollMessageFromGroup(MESSAGE_GROUP);
assertNotNull(messageFromStore);
assertEquals(message, messageFromStore);
}
@Configuration
@EnableIntegration
public static class ContextConfiguration {
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public ChannelMessageStoreQueryProvider queryProvider() {
return new H2ChannelMessageStoreQueryProvider();
}
@Bean
public JdbcChannelMessageStore jdbcChannelMessageStore() {
JdbcChannelMessageStore jdbcChannelMessageStore = new JdbcChannelMessageStore(dataSource);
jdbcChannelMessageStore.setChannelMessageStoreQueryProvider(queryProvider());
return jdbcChannelMessageStore;
}
@Bean
public PollableChannel transactionalQueueChannel() {
return new QueueChannel(new MessageGroupQueue(jdbcChannelMessageStore(), MESSAGE_GROUP));
}
@Bean
public TransactionInterceptor transactionInterceptor() {
return new TransactionInterceptorBuilder()
.transactionManager(transactionManager())
.isolation(Isolation.READ_COMMITTED)
.propagation(Propagation.REQUIRED)
.build();
}
@Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(5);
return threadPoolTaskExecutor;
}
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller() {
return Pollers.fixedDelay(10)
.advice(transactionInterceptor())
.taskExecutor(threadPoolTaskExecutor())
.get();
}
@Bean
public CountDownLatch exceptionLatch() {
return new CountDownLatch(2);
}
@ServiceActivator(inputChannel = "transactionalQueueChannel")
public void handle(Message<?> message) {
System.out.println(message);
try {
throw new RuntimeException("Intentional for rollback");
}
finally {
exceptionLatch().countDown();
}
}
}
}
@RunWith(SpringRunner.class)
@肮脏的环境
公共类SO47264688测试{
私有静态最终字符串消息\u GROUP=“transactionalQueueChannel”;
私有静态嵌入式数据库数据源;
@课前
公共静态void init(){
dataSource=新的EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript(“classpath:/org/springframework/integration/jdbc/schema-drop-h2.sql”)
.addScript(“classpath:/org/springframework/integration/jdbc/schema-h2.sql”)
.build();
}
@下课
公共静态无效销毁(){
dataSource.shutdown();
}
@自动连线
专用PollableChannel transactionalQueueChannel;
@自动连线
私有JdbcChannelMessageStore JdbcChannelMessageStore;
@自动连线
私有轮询消费者服务激活端点;
@自动连线
私人倒计时闩锁例外情况;
@试验
public void testTransactionalQueueChannel()引发InterruptedException{
GenericMessage message=新的GenericMessage(“foo”);
this.transactionalQueueChannel.send(消息);
assertTrue(此.exceptionLatch.await(10,TimeUnit.SECONDS));
this.serviceActivationEndpoint.stop();
assertEquals(1,this.jdbcChannelMessageStore.messageGroupSize(MESSAGE_GROUP));
Message messageFromStore=this.jdbcChannelMessageStore.pollMessageFromGroup(消息组);
assertNotNull(messageFromStore);
assertEquals(message,messageFromStore);
}
@配置
@使能集成
公共静态类上下文配置{
@豆子
公共平台transactionManager transactionManager(){
返回新的数据源TransactionManager(数据源);
}
@豆子
公共频道MessageStoreQueryProvider queryProvider(){
返回新的H2ChannelMessageStoreQueryProvider();
}
@豆子
公共JdbcChannelMessageStore JdbcChannelMessageStore(){
JdbcChannelMessageStore JdbcChannelMessageStore=新的JdbcChannelMessageStore(数据源);
setChannelMessageStoreQueryProvider(queryProvider());
返回jdbcChannelMessageStore;
}
@豆子
public PollableChannel transactionalQueueChannel(){
返回新的QueueChannel(新的MessageGroupQueue(jdbcChannelMessageStore(),MESSAGE_GROUP));
}
@豆子
公共事务接收器TransactionInterceptor(){
返回新的TransactionInterceptorBuilder()
.transactionManager(transactionManager())
.isolation(隔离.READ_提交)
.传播(需要传播)
.build();
@Autowired
PlatformTransactionManager transactionManager;
@Bean
public TransactionInterceptor transactionInterceptor() {
return new TransactionInterceptorBuilder(true)
.transactionManager(transactionManager)
.isolation(Isolation.READ_COMMITTED)
.propagation(Propagation.REQUIRED)
.build();
}
@RunWith(SpringRunner.class)
@DirtiesContext
public class So47264688Tests {
private static final String MESSAGE_GROUP = "transactionalQueueChannel";
private static EmbeddedDatabase dataSource;
@BeforeClass
public static void init() {
dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:/org/springframework/integration/jdbc/schema-drop-h2.sql")
.addScript("classpath:/org/springframework/integration/jdbc/schema-h2.sql")
.build();
}
@AfterClass
public static void destroy() {
dataSource.shutdown();
}
@Autowired
private PollableChannel transactionalQueueChannel;
@Autowired
private JdbcChannelMessageStore jdbcChannelMessageStore;
@Autowired
private PollingConsumer serviceActivatorEndpoint;
@Autowired
private CountDownLatch exceptionLatch;
@Test
public void testTransactionalQueueChannel() throws InterruptedException {
GenericMessage<String> message = new GenericMessage<>("foo");
this.transactionalQueueChannel.send(message);
assertTrue(this.exceptionLatch.await(10, TimeUnit.SECONDS));
this.serviceActivatorEndpoint.stop();
assertEquals(1, this.jdbcChannelMessageStore.messageGroupSize(MESSAGE_GROUP));
Message<?> messageFromStore = this.jdbcChannelMessageStore.pollMessageFromGroup(MESSAGE_GROUP);
assertNotNull(messageFromStore);
assertEquals(message, messageFromStore);
}
@Configuration
@EnableIntegration
public static class ContextConfiguration {
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public ChannelMessageStoreQueryProvider queryProvider() {
return new H2ChannelMessageStoreQueryProvider();
}
@Bean
public JdbcChannelMessageStore jdbcChannelMessageStore() {
JdbcChannelMessageStore jdbcChannelMessageStore = new JdbcChannelMessageStore(dataSource);
jdbcChannelMessageStore.setChannelMessageStoreQueryProvider(queryProvider());
return jdbcChannelMessageStore;
}
@Bean
public PollableChannel transactionalQueueChannel() {
return new QueueChannel(new MessageGroupQueue(jdbcChannelMessageStore(), MESSAGE_GROUP));
}
@Bean
public TransactionInterceptor transactionInterceptor() {
return new TransactionInterceptorBuilder()
.transactionManager(transactionManager())
.isolation(Isolation.READ_COMMITTED)
.propagation(Propagation.REQUIRED)
.build();
}
@Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(5);
return threadPoolTaskExecutor;
}
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller() {
return Pollers.fixedDelay(10)
.advice(transactionInterceptor())
.taskExecutor(threadPoolTaskExecutor())
.get();
}
@Bean
public CountDownLatch exceptionLatch() {
return new CountDownLatch(2);
}
@ServiceActivator(inputChannel = "transactionalQueueChannel")
public void handle(Message<?> message) {
System.out.println(message);
try {
throw new RuntimeException("Intentional for rollback");
}
finally {
exceptionLatch().countDown();
}
}
}
}