前景:开了一个接口给第三方回调,调用频率是大概每两毫秒一次调用,
第三方每天早上九点到下午两点执行调用,为了高性能,处理接收方式是数据直接存储至mongo,在下午三点开启定时器再解析mongo中的json数据转化为结构化数据存储至mysql给数据分析师进行分析;
解析数据过程:经过几天的第三方调用发现每天大概数据量在五十万条上下浮动,数据分析师需要每天分析当天的数据凌晨两点发送分析结果,经过几天的转换结构化数据后得出每天下午三点开始每隔一分钟执行一次定时器,每次从mongo中查询五千条数据然后开一个线程池进行转化,开了线程池后五千条由非结构化转化为结构化大概在二十多秒上下浮动;
场景问题:由于每一条数据要么转化成功要么失败回滚,所以要考虑事务问题,然后又开启了线程池,所以需要考虑多线程中单个线程的事务控制
起初是在线程调用的方法下加上事务的注解经过测试发现事务无效,后来百度说需要父级方法和子级方法设置事务配合使用,试过也无效,还有因为多线程调用方法的对象需要重新从容器中获取否则事务失效,我也试过可能方法不对还是事务失效,经过多次尝试发现了一种有效事务控制方式,上代码
下面是创建一个线程池,200数量忽略
ExecutorService service = Executors.newFixedThreadPool(200);
try {
for (AICallInfoLog callInfoLog : dataWaitingHandle) {
service.execute(() -> handleCallInfo(mongoTemplate, callInfoLog));
}
service.shutdown();
service.awaitTermination(dataWaitingHandle.size(), TimeUnit.MINUTES);
} catch (Exception e) {
e.printStackTrace();
service.shutdown();
} finally {
service.shutdown();
}
调用对应处理方法
@Override
public void handleCallInfo(MongoTemplate mongoTemplate, AICallInfoLog callInfoLog) {
if (ObjectUtil.isNotNull(callInfoLog)) {
//解析数据为结构化数据
if (analysisAICallBackData(callInfoLog)) {
writeBackMongo(callInfoLog, mongoTemplate);
callInfoLog = null;
}
}
}
具体业务逻辑代码入口处做事务管理
private final PlatformTransactionManager transactionManager;
public Boolean analysisAICallBackData(AICallInfoLog callInfoLog) {
//处理多线程中单个线程的事务
AtomicBoolean rollBackFlag = new AtomicBoolean(false);
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus status = transactionManager.getTransaction(def);
try {
CallEndCallBackReq req = JSONObject.parseObject(callInfoLog.getCallBackData(), CallEndCallBackReq.class);
if (Convert.toInt(req.getBill()) > 0) {
analysisAICallDataBody(req);
} else {
updateAIUserInfo(req);
// int a = 1/0;事务测试
}
if(rollBackFlag.get()){
transactionManager.rollback(status);
}else {
transactionManager.commit(status);
}
} catch (Exception e) {
log.info("解析数据报错:{}", e.getMessage());
e.printStackTrace();
//如果出错了 就放开锁 让别的线程进入提交/回滚 本线程进行回滚
rollBackFlag.set(true);
transactionManager.rollback(status);
return false;
}
return true;
}
这样就实现了多线程单个线程事务异常只会回滚对应的那个线程的事务,不影响其他线程操作,
回滚后的数据会在下一次的定时器中继续被调用