java-采用多线程方式优化实现aop日志切面的用户操作记录

该博客介绍了如何通过AOP切面在Spring框架中实现用户操作记录,并利用异步线程池进行性能优化。在高并发场景下,为避免日志保存操作影响业务执行,将日志保存放入单独的线程中处理,确保了主业务流程的高效运行。同时,展示了具体的切面类、日志处理线程池和线程任务的实现细节。
摘要由CSDN通过智能技术生成

说明:

一般情况下,我们项目都会保留用户的操作记录,而最简单有效的方案就是,aop切面,
但是:
使用aop切面保留用户操作记录的原理:
是采用代理模式实现了功能增强,
生成了一个代理对象,在原有的功能(也就是我们的业务逻辑)上,添加了日志记录的功能实现;

是的没问题,方案ok;
但是有一个问题:保存操作记录的这部分操作也是需要时间的,虽然正常情况下都很短,就是往数据库insert一条数据,但是还是会有效率的影响;

在极高并发的情况下,也是有可能因为插入操作记录的这段时间-导致整段业务的异常回滚;

所以:使用异步线程来优化aop切面;

代码如下:

1.切面类;

注意Pointcut切点配置时,配置目标拦截的包;
不要拦截了操作记录插入的操作,不然自己拦截了自己肯定就死循环了;

/**
 * 系统日志,切面处理类
 *
 * @author 
 */
@Aspect
@Component
public class SysLogAspect {

    /**
     * 日志处理类
     */
    @Autowired
    private DefaultWebLogHandler webLogHandler;

    //    @Pointcut("@annotation(com.msun.mrs.common.annotation.mrLog)")
    @Pointcut("execution(* com.msun.mrs.business.server.*.controller.*.*(..))")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //执行方法
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
        try {
            saveSysLog(point, time);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        SysLogEntity sysLog = new SysLogEntity();

        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        sysLog.setClassMethod(className);
        sysLog.setClassMethod(methodName);
        //获取request
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if(request == null) return;
        sysLog.setHttpMethod(request.getMethod());

        //请求的参数
        Object[] args = joinPoint.getArgs();

        try {
            if (ArrayUtils.isNotEmpty(args)) {
                String params = new Gson().toJson(args[0]);
                if(StringUtils.isNotEmpty(params) && params.length()<4096) {
                    sysLog.setRequestParams(params);
                }else {
                    sysLog.setRequestParams(StringZipUtils.compactString(params));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //设置IP地址
        sysLog.setIp(IPUtils.getIpAddr(request));
        //设置url
        sysLog.setUrl(request.getRequestURL().toString());
        //用户名
        Long userId = ContextVariableThreadLocal.getUserId();
        sysLog.setUserId(String.valueOf(userId));
        sysLog.setTimeCost(Integer.parseInt(String.valueOf(time)));
        sysLog.setCreateTime(new Date());
        //保存系统日志
        webLogHandler.persistenceLog(sysLog);
    }
}

2.日志线程池处理器

其中线程池,简单操作就直接创建了:Executors.newFixedThreadPool(10);
如有需要也可以使用配置文件,项目加载的形式,一样的;

/**
 * @author weixz
 * @date 2021/3/26 9:00
 */
@Component
public class DefaultWebLogHandler implements WebLogHandler<SysLogEntity> {

    @Autowired
    private SysLogService sysLogService;

    private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);

    @Override
    public void persistenceLog(SysLogEntity sysLogEntity){
        fixedThreadPool.execute(new OperationLogThread<>(sysLogService,sysLogEntity));
//        sysLogService.save(sysLogEntity);
    }
}

3.日志线程任务

/**
 * @author weixz
 * @date 2021/3/26 8:55
 */
@Slf4j
public class OperationLogThread<T> implements Runnable {

    private volatile SysLogService sysLogService;
    private volatile SysLogEntity sysLogEntity;

    public OperationLogThread(SysLogService sysLogService,
                              SysLogEntity sysLogEntity) {
        this.sysLogService=sysLogService;
        this.sysLogEntity=sysLogEntity;
    }

    @Override
    public void run() {
        try {
            this.sysLogService.save(sysLogEntity);
        } catch (Exception e) {
            log.error("thread name "+Thread.currentThread().getName()+" save sysLog error",e);
            log.error("save sysLog error Url: " + sysLogEntity.getUrl());
        }finally {
            sysLogEntity = null;
        }
    }
}

其中:

SysLogService : 操作记录对应的service;也可以直接用dao-save一样的,在此处的功能就是保存到数据库;
SysLogEntity : 操作记录对应的实体;
不做详细展示了,需要什么保留什么就行;

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值