记一次多线程事务提交导致内存溢出的问题

目录

一.业务背景

1.概要

2.业务说明

二.具体实现

三.问题现象

四.后续


一.业务背景

1.概要

      我们的系统是HIS(Hospital information system)系统,也就是医院信息化系统。HIS系统由很多子系统组成,比如:面向医生使用的医生站系统、面向收费使用的收费系统、面像护士使用的护士站系统;按照场景又分门诊医生站、住院医生站。由于我们是微服务架构,所以每个微服务基本上也就是一个子系统。今天的主角是门诊医生站。

2.业务说明

    门诊医生站主要用户为医生,医生主要的工作就是开医嘱,医嘱又分为检查、检验、药品、治疗、手术等等多种类型的医嘱。医生开医嘱的过程是一个个开,然后保存,保存的过程称为草稿,可以开很多的草稿,然后一块提交,提交也就是正式生效

二.具体实现

   因为担心医生开立很多草稿一块提交,导致数据量过大,系统响应慢,所以此处使用的多线程提交,多线程的维度是有几种类型的医嘱就开几个线程,同时多线程提交还要考虑事务一致性的问题,所以多线程使用countdownlatch来控制控制线程统一提交或者回滚,系统又使用ThreadLocal存储线程级的共享变量,需要将ThreadLocal传递给子线程

if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_1)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes, transFlag, transDrug)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_2)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transExam)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_3)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transInspect)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_4)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transTreat)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_11)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transPathology)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_9)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transConsultation)));
}

 AsynCallable为自定义线程,trans开头为具体实现类

public class AsynCallable implements Callable<Object>, IThreadCacheService {

    //事物执行体,也就是具体医嘱类型
    private AbstractTransService service;
    //事物标志
    private TransFlag transFlag;

    private RequestAttributes requestAttributes;


    public AsynCallable(RequestAttributes parentRequestAttributes, TransFlag transFlag, AbstractTransService service){
        this.service = service;
        this.transFlag = transFlag;
        this.requestAttributes = parentRequestAttributes;

    }
    @Override
    public Object call() throws Exception {
        try {
            RequestContextHolder.setRequestAttributes(this.requestAttributes);
            putDtSign(transFlag.getDtSign());
            Object o = service.lockExec(transFlag,this.service.getClass());
            return o;
        } finally {
            drugItemsTl.remove();
            dtSignThread.remove();
            serialNoThread.remove();
            orderGroupThread.remove();
            RequestContextHolder.resetRequestAttributes();
        }
    }
}

/**
 * @Author 
 * @Date 2020-1-9 17:14
 * 多线程提交事物保证抽象类
 */
public abstract class AbstractTransService<T> {

    @Transactional(rollbackFor = Exception.class)
    public T lockExec(TransFlag transFlag,Class clazz) throws ExecutionException, InterruptedException {
        T t = null;
        try {
            t = doExec(transFlag.getIdVisit(),transFlag.getIdDeptSign(),transFlag.getSignDeptName());
        } catch (Exception e) {
            LogUtil.error("-------当前医嘱提交失败---thread:",e);
            transFlag.setAllSuccess(false);
            transFlag.getCountDownLatch().countDown();
            transFlag.getSdClinicTypeClass().add(clazz);
            if(e instanceof ExecutionException){
                ExecutionException ee = (ExecutionException)e;
                if(ee.getCause() instanceof CustomVerify){
                    CustomVerify cv = (CustomVerify)ee.getCause();
                    transFlag.setMsg(cv.getMsg());
                }else{
                    transFlag.setMsg(ee.getMessage());
                }
            }else{
                transFlag.setMsg(e.getMessage());
            }
            LogUtil.error("-------当前医嘱提交失败---thread:["+Thread.currentThread().getName()+"]:"+e.getMessage());
            throw e;
        }

        try {
            transFlag.getCountDownLatch().countDown();
            transFlag.getSdClinicTypeClass().add(clazz);
            transFlag.getCountDownLatch().await();
            if(!transFlag.isAllSuccess()){
                LogUtil.error("--------有未成功提交的医嘱,回滚......");
                throw new CustomVerify(GlobalReturnCode.SYSTEM_PARA_ERROR.getCode(),"提交失败");
            }
        } catch (InterruptedException e) {
            transFlag.setAllSuccess(false);
            LogUtil.error("线程间通讯失败:",e);
            throw new CustomVerify(GlobalReturnCode.SYSTEM_PARA_ERROR.getCode(),"提交失败");
        }
        return t;
    }

    /**
     * 具体业务逻辑,必须实现
     * @param idVisit
     * @param idDeptSign
     * @param signDeptName
     * @return
     */
    public abstract T doExec(String idVisit,String idDeptSign,String signDeptName) throws ExecutionException, InterruptedException;
}

三.问题现象

     在开启多线程的情况偶尔会出现线程不动的情况,之所以说“出现线程不动”,是因为至今也没有找到具体的原因,猜测是数据库死锁导致,但是多线程操作时每个线程只会更新一种类型的医嘱,不会出现多个线程更新同一个记录的情况,这也是至今想不明白的地方,而且测试也无法复现

 图中 58号线程已经不动了,只打出一条update语句,更新就卡住了......

四.后续

     因为这个原因被领导要求反思,最后,只能先由多线程提交改为单线程提交,问题不再出现,但是速度......单线程优化吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hamilton_Huan

原创不易,结合业务原创更不易

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值