分布式事务 (定时任务 + 事件表 + MQ)实现小例子
业务流程图:
Controller 模拟接收请求 生成 100条数据 插入事件表:
@RequestMapping("/producerMq") public @ResponseBody String mq() throws Exception { for(int i = 0;i<100;i++){ Long userId = SnowFlake.nextId();//雪花算法生成id AopExsInfo aopExsInfo = new AopExsInfo(); aopExsInfo.setId(userId); aopExsInfo.setType("new"); //状态对应为 新建 int Anum = aopExsInfoMapper.insert(aopExsInfo); //插入事件表 // System.out.println("新增结果: "+Anum); } return "新增完成"; }
服务A:
package com...service_RabbitMQ; import com.alibaba.fastjson.JSONObject; import com...entity.AopExsInfo; import com...mapper.AopExsInfoMapper; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import javax.annotation.PostConstruct; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Component public class SendMqService { @Autowired AopExsInfoMapper aopExsInfoMapper; @Autowired private AmqpTemplate template; //项目启动自动循环执行 //无论哪一步出异常都全部回滚,不影响数据一致性 @PostConstruct @Transactional(rollbackFor = Exception.class) public void sendData(){ System.out.println("开始扫描未推送的办件...."); ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { //查出新增的数据 List<AopExsInfo> list = aopExsInfoMapper.selectNew("new"); for (AopExsInfo aopExsInfo : list){ //先改事件表数据状态 AopExsInfo old = new AopExsInfo(); old = aopExsInfo; old.setCupSize("old"); //对应为 已发送 int i = aopExsInfoMapper.updateByPrimaryKey(old); // System.out.println("更改事件表状态返回: "+i); //开始往MQ里推送 JSONObject json = new JSONObject(); json.put("id",aopExsInfo.getId()); json.put("cupSize",aopExsInfo.getCupSize()); // System.out.println("未推送的办件信息 : "+json.toString()); template.convertAndSend(RabbitConstant.QUEUE_HELLOWORLD,json.toString()); } } },0,10, TimeUnit.SECONDS); //每10秒 查询推送 一次 } }
服务B:
package com...service_RabbitMQ; import com.alibaba.fastjson.JSONObject; import com.rabbitmq.client.Channel; import com...entity.XueHuaInfo; import com...mapper.XueHuaInfoMapper; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import javax.annotation.PostConstruct; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Component public class AcceptMqService{ @Autowired XueHuaInfoMapper xueHuaInfoMapper; //从MQ读取数据,插入事件表 @RabbitListener(queues = RabbitConstant.QUEUE_HELLOWORLD) public void acceptData(String info, Channel channel, Message message){ try { //手动 ACK // channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); System.out.println("监听到的数据: "+info); // System.out.println("message : " +message); JSONObject JSON = JSONObject.parseObject(info); XueHuaInfo xueHuaInfo = new XueHuaInfo(); xueHuaInfo.setId(JSON.getString("id")); //根据数据库主键唯一保证幂等性 xueHuaInfo.setName("start"); //对应状态为 已接受 xueHuaInfoMapper.insert(xueHuaInfo); } catch (Exception e) { e.getMessage(); } } //项目启动自动执行 @PostConstruct @Transactional(rollbackFor = Exception.class) public void updataData(){ System.out.println("开始执行本地业务...."); ScheduledExecutorService Service = Executors.newScheduledThreadPool(2); Service.scheduleAtFixedRate(new Runnable() { @Override public void run() { //查出新增的数据 List<XueHuaInfo> infos = xueHuaInfoMapper.selectNew("start"); System.out.println("本次读取办数量: "+infos.size()); for(XueHuaInfo info : infos){ //业务处理 省略.... XueHuaInfo endInfo = info; endInfo.setName("end");//对应状态为 已处理 xueHuaInfoMapper.updateEnd(endInfo); } } },0,10, TimeUnit.SECONDS); ////每10秒查询一次 } }