为什么Amazon sqs不支持交易?

时间:2018-10-22 18:51:44

标签: spring amazon-web-services rabbitmq activemq amazon-sqs

在春季,Rabbitmq支持交易。但是,Amazon sqs在春季不支持交易。

对不起。我添加了更多内容。 春季,我测试了两个消息队列(rabbitmq,amazon sqs),如下所示。

我的目的是在用户无例外完成注册后处理将用户电子邮件发送到队列以发送注册完成电子邮件的逻辑。

//rabbit mq configuration.class
@Bean
public ConnectionFactory rabbitConnectionFactory() {
    CachingConnectionFactory connectionFactory =
            new CachingConnectionFactory("localhost",5672);
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    return connectionFactory;
}

@Bean
public SimpleMessageListenerContainer messageListenerContainer() {
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(rabbitConnectionFactory());
    container.setQueueNames(queue);
   container.setMessageListener(exampleListener());
    container.setTransactionManager(platformTransactionManager);
    container.setChannelTransacted(true);
    return container;
}


@Bean
public RabbitTemplate producerRabbitTemplate() {
    RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
    rabbitTemplate.setQueue(queue);
    rabbitTemplate.setMandatory(true);
    rabbitTemplate.isChannelTransacted();
    rabbitTemplate.setChannelTransacted(true);
    return rabbitTemplate;
}



//UserService.class
@Autowired
private final UserRepository userRepository;
@Autowired
private final RabbitTemplate rabbitTemplate;

@Transactional
public User createUser(final User user){
    rabbitTemplate.convertAndSend("spring-boot", user.getEmail()); // SignUp Completion email
    final User user = userRepository.save(user);
    if(true) throw new RuntimeException();
    return user;
}

但是,以上逻辑发生了runtimeException。

如果发生异常,由于Spring中的事务注释,兔子mq将不会将数据发送到队列。

//amazon sqs configuration.class
@Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSqs) {

//UserService.class
@Autowired
private final UserRepository userRepository;
@Autowired
private QueueMessagingTemplate messagingTemplate;


@Transactional
public User createUser(final User user){
    messagingTemplate.convertAndSend("spring-boot", user.getEmail()); // SignUp Completion email
    final User user = userRepository.save(user);
    if(true) throw new RuntimeException();
    return user;
}

但是,即使发生异常,sq也会将数据发送到队列。

有人知道这是为什么吗?

1 个答案:

答案 0 :(得分:1)

TLDR如何解决此问题?

不要为此使用事务,而是想出一些方法使系统最终保持一致。


听起来像您想执行'queue.sendMessage'和'repository.save'就像它们是事务一样-要么都提交,要么都不提交。问题在于,即使在您的兔子示例中,“交易”也不是真正的交易。

考虑事务处理的基本原理:

  1. 某种begin步骤,表示以下更改是交易的一部分
  2. 进行了更改(但对读者不可见)
  3. 某种commit步骤,自动提交更改(使更改对读者可见)

但是,在您的情况下,队列和存储库是独立的实体,并由独立的网络资源作为后盾,彼此之间不会互相通信。在这种情况下,没有原子提交。没有原子提交,您将无法进行真正的交易。它在您的演示中“起作用”,因为异常与执行写操作的代码是分开的。

考虑这种情况以更清楚地说明:

@Transactional
public User createUser(final User user){
    messagingTemplate.convertAndSend("spring-boot", user.getEmail());
    final User user = userRepository.save(user);
    return user;
}
  • 为此开始执行提交步骤时,它需要使消息(在队列中)和用户(在仓库中)都可见
  • 为此,需要同时对队列和仓库进行网络调用
    • 这是两个电话的事实是问题的根源
  • 如果一个成功而另一个失败,那么您将处于不一致状态
    • 您可能会说“事务可以回滚”-但是回滚将涉及另一个网络调用,这当然会失败。

有多种方法可以使整个分布式系统具有事务性,但这是very complex problem。允许暂时的不一致并拥有使系统最终保持一致的机制通常会更容易,更快捷。