自定义注解+反射+AOP+异步多线程统一日志管理

多线程原因:

导致高并发情况下接口性能下降。

难点:

高并发下的异步线程记录

SpringUtils 研究下

线程池:主业务记个日志,异步需要给系统有个间隔切换,一般10ms。

线程池全局只能有1个,类似一个大别墅,只有一个池

线程池参数如何设置?不能写死!!!

高大上:分布式配置中心+动态线程池

标准:yml文件读取线程池配置【写个类封装 专门读取配置】 跟主业务无关的 按面向对象思想封装OOP

不要用超过5个@Value来获取值 应该单独读到配置类里使用更合适

yaml配置

thread:
  pool:
    core-pool-size: 5
    # 默认是5个人记录 如果是高并发的话直接把下面两个放大到1000和5000 如果1秒来了1w个
    # 我有1000个人来记录 性能就上来了
    max-pool-size: 10
    queue-capacity: 50
    keep-alive-seconds: 60
@Confighration
@ConfigurationProperties(prefix = "thread.pool")【yaml配置里的前缀】
public class ThreadPoolProperties{
    private int corePoolSize;
    private int maxPoolSize;
}

还需要个线程池类

@Configuration
public class ThreadPoolConfig{
    // 线程池配置
    @Resource
    private ThreadPoolProperties threadPoolProperties;
    // 创建线程池 最通用 最标准 普通版
    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程池大小
        executor.setCorePoolSize(threadPoolProperties.getCorePoolSize());
        // 最大可创建线程数
        executor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize());
        // 等待队列最大长度
        executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
        // 线程池维护线程所允许的空闲时间
        executor.setKeepAliveSconds(threadPoolProperties.getKeepAliveSeconds());
        // 线程池对拒绝任务(无线程可用的处理策略)
        executor.setRejectedExectiomHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
        
    }

    
    // 定时任务版 线程池里的线程每隔10ms或1秒执行一次
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService(){
        return new ScheduledThreadPoolExecutor(threadPoolProperties.getCorePoolSize(),
                                               new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
                                              new ThreadPoolExecutor.CallerRunsPolicy()){
                                        @Override
                                        protected void afterExecute(Runnable r,Throwable t){
                                            super.afterExecute(r,t);
                                            ThreadUtils.printException(r,t);
                                        }
        };
    }
}
    
    

面试:线程池你如何使用?

1.YML配置线程池核心参数

2.不可以多个@Value读取,多个配置项使用ThreadPoolProperties封装进一个具体的配置类

3.ThreadPoolConfig封装好的线程池对象里面要有2个方法,

1.普通调用

2.定时间隔调用

这里当请求过来后5-10ms后才启动异步定时任务的线程去插入日志,有点时间间隔,不要让系统负担太重

异步工厂

public class AsyncFactory {
    // 记录操作日志 OperationLogVo就是数据库操作日志表对应的那个对象
    public static TimerTask recordOperation(OperationLogVo operationLog){
        // 这里其实是java8写法 new了个TimeTask定时任务
        return () -> {
            // 容器捞鱼🐟 再save
            SpringUtils.getBean(OperationLogService.class).saveOperationLog(operationLog);
        };
    }
}

@Service
public class OperationLogService{
    @Resource
    private OperationLogVoMapper operationLogVoMapper;

    public void saveOperationLog(OperationLogVo operationLogVo){
        operationLogVoMapper.insertSelective(operationLogVo);
    }
}

异步管理层

// 因为线程只需要有一个AsyncManager来管理就够了
public class AsyncManager {
    // 单例模式确保类只有1个实例
    private AsyncManager(){}
    // 饿汉式 在类加载的时候立刻实例化
    private static final AsyncManager INSTANCE = new AsyncManager();
    public static AsyncManager getInstance(){return INSTANCE;}
    // 异步操作任务调度线程池
    private final ScheduledExecutorService scheduledExecutorService = SpringUtils.getBean("ScheduledExecutorService");
    
    // 执行任务
    public void execute(TimerTask task){
        scheduledExecutorService.schedule(task,10,TimeUnit.MILLISECONDS);
    }
    // 停止任务线程池
    public void shutdown(){
        ThreadUtils.shutdownAndAwaitTermination(scheduledExecutorService);
    }

1.异步管理层AsyncManager

2.异步工厂直接生成异步任务,配合自定义间隔时间的线程池使用,每次TimeTask,每次间隔10毫秒做一次操作,多线程来完成。

@Ansy不灵活,AnsyFactory和AnsyManager更加灵活使用

切面

只要用到自定义注解就想到反射+aop

然后想到joinPoint.getSignature()方法签名【调用的什么方法 比如Controller的addUser方法】

joinPoint.getTarget().getClass().getSimpleName(); 获得当前正在使用的那个类 还可以拿到类名

//  这里就不写pointCut了 只要带有OperationLog注解 就执行环绕通知
@Around("@annotation(com.xxx.xxx.annotation.OperationLog)")
// 环绕通知
// 容器捞鱼设计思想 
public Object around(ProceedingJoinPoint joinPoint){
    Object result = null;
    // 1.获取方法名
    String methodName = joinPoint.getTarget().getClass().getSimpleName(); 
    try{
         // 2.记录操作日志 拼装Log对象
        OperationLogVo operationLogVo = this.recordLog(JoinPoint);
       
    }catch(Exception e){
        log.error("没有合适的web请求",e);
    }

    // 3.记录业务耗时和其他参数等
    try{
        result = joinPoint.proceed();
    }catch(Throwable e){
        // 这里异常用的是commons-lang3 依赖 封装好的
        log.error("method:{},throws:",methodName,ExceptionUtils.getStackTrace(e));
        if(operationLogVo != null){
            // 执行错误也要记录日志
            operationLogVo.setErrorMsg();
        }
    }finally{
        
    }
}

    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值