【RabbitMq/Springboot】利用RabbitMq实现跨库转账的原理性实现

本文涉及RabbitMq版本3.7.18,Springboot版本:2.5.4。

 

之前我们探讨了单库转账,这回准备来实现一个跨库转账,实现声明这只是原理性实现,不是最终版本。

实现的想法是在A机的本地账户扣款,然后把远程账户和加款金额发到队列里,B机接到消息后更新本地库的对应账户。

A机为T440p,资金转出账户001,消息生产者和RabbitMq都在里面;B机为T14,转入账户002、消息消费者在里面。

 

A机实现分以下几步:

1.准备远程转账函数

@Component
public class AccountService {
    @Resource
    private AccountMapper amapper=null;
    
    @Autowired
    private RabbitMqMsgSender mqSender;
    
    @Transactional(rollbackFor=Exception.class) 
    public void remoteTransfer(int amount,String fromAccount,String remoteAcccount) throws TransferException{
        int count=amapper.add(-amount, fromAccount);
        if(count==0) {
            throw new TransferException("对转出账户:"+fromAccount+"操作,更新记录数为0.只有可能是该账户不存在。");
        }
        
        mqSender.send(remoteAcccount+"/"+amount);
    }
}

可以看到原来给转入加款的部分被替换成了往消息队列里发消息,消息格式为:远程账户/加款金额。

 

2.在测试函数中执行

@SpringBootTest
class MyBankApplicationTests {

    @Autowired
    private AccountService aService;
    
    @Test
    void test() {
        try {
            aService.remoteTransfer(100, "001", "002");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这个也可以点击页面按钮通过Ajax调用函数什么的,为了简便就直接在测试类里做了。

3.看看数据库情况:

看001账户被扣款一百,至少本地是对的。

 

B机实现分以下几步:

 1.收到消息后对数据库进行处理

import javax.annotation.Resource;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import com.hy.mybank.mapper.AccountMapper;

@Component
@RabbitListener(queues="queue01")
public class RabbitMqMsgReceiver {
    @Resource
    private AccountMapper amapper=null;
    
    @RabbitHandler
    public void QueueReceive(String receivedMsg) {
        System.out.println("收到消息:"+receivedMsg);
        
        String[] arr=receivedMsg.split("[//]");
        String toAccount=arr[0];
        int amount=Integer.parseInt(arr[1]);
        
        amapper.add(amount, toAccount);
    }
}

这边的处理也简单,直接劈分消息,第一个元素为转入账户,第二个元素为金额,进行数据操作即可。

值得注意的是,QueueReceive函数一旦出现任何异常,队列里的消息是不会丢失的。我在劈分receivedMsg后写错序号,导致下标越界异常,但队列里面的消息还在,虽然它已经被取到劈分过了,这说明这个函数一有异常,消息是会被退回队列的。加上队列里消息存储机制,可以说安全性至少有两层。

2.add函数

@Mapper
public interface AccountMapper {

    @Update("Update account set balance=balance+#{count} where customer_id=#{customer_id}")
    int add(int count,String customer_id);

}

这个函数就是操作数据库的SQL,没啥好说的。

 

3.数据库情况

可以看到002账户增加了100元,这个帐是平的了。

 

以上即为跨库转账的原理性实现,这个实现当然是有漏洞的,比如转入账户不存在,转出账户的钱岂不是消失了,怎么保证远程转账的原子性,是接下来需要思考的问题。

-END-

 

posted @ 2021-09-10 19:17  逆火狂飙  阅读(174)  评论(0编辑  收藏  举报
生当作人杰 死亦为鬼雄 至今思项羽 不肯过江东