0
点赞
收藏
分享

微信扫一扫

任务中断的两套API函数(改进FormISR的实时性)资源管理_互斥操作的本质(解决DH11经常出错的问题)

兵部尚输 2024-11-24 阅读 9

背景

在实际的开发中,可能一个用户会调用不同的三方接口进行获取数据,比如针对银行接口有发起授信申请、授信查询、借款申请借、借款查询。 那么如何统计每个API的三方耗时。

@Component
public class UserServiceImpl implements UserService{

    @Override
    public void creditApply() {
    	// 模拟调用三方
        System.out.println("creditApply");
    }

    @Override
    public void creditQuery() {
    	// 模拟调用三方
        System.out.println("creditQuery");
    }

    @Override
    public void loanApply() {
        // 模拟调用三方
        System.out.println("loanApply");
    }

    @Override
    public void loanQuery() {
        // 模拟调用三方
        System.out.println("loanQuery");
    }
}

显然这个耗时统计是一个非功能性需求,那么我们考虑几种方案

方案1

比较简单粗暴,那就是在每个接口调用时 直接编码统计,优点是简单 好实现,缺点是和业务代码强耦合在一起,显然不是一个好的方案

方案2

既然想剥离开,那么简单的方式就是SpringAOP机制,进行切面编程,将公共的统计耗时逻辑进行抽取出来,这里其实又遇到一个问题,那就是,如果只是单纯的计算一个接口耗时,没有问题,如果想计算一个用户请求,所有的三方API耗时,就需要串连起来,那么既然同一个用户请求 如果不是异步处理,那么必定在一个线程内,所以就可以结合ThreadLocal使用。

方案2 code

定义耗时统计map,这里有个注意点就是 因为要保证添加到Map中的有序性,所以使用 LinkedHashMap

public class ApiTimeMap {

    private Map<String, BigDecimal> map;

    public ApiTimeMap() {
        map = new LinkedHashMap<>();
    }

    public void add(String key, BigDecimal value) {
        map.put(key, value);
    }

    public Map<String, BigDecimal> getMap() {
        return map;
    }
}

定义ThreadLocal 保存的是一个Map

public class TimeHolder {

    private static final ThreadLocal<ApiTimeMap> timeThreadLocal = new ThreadLocal<>();

    public static void setMethodTime(String method, BigDecimal time) {
        ApiTimeMap apiTimeMap = timeThreadLocal.get();
        if (Objects.isNull(apiTimeMap)) {
            apiTimeMap = new ApiTimeMap();
        }
        apiTimeMap.add(method, time);
        timeThreadLocal.set(apiTimeMap);
    }

    public static ApiTimeMap getThreadApiTimeMap() {
        return timeThreadLocal.get();
    }

    public static void clear() {
        timeThreadLocal.remove();
    }

}

核心切面类

**
 * @author qxlx
 * @date 2024/11/22 21:52
 */
@Aspect
@Component
public class TimeAspect {

    private static final Logger log = LoggerFactory.getLogger(TimeAspect.class);

    @Pointcut("execution(* com.qxlx.clactime.UserServiceImpl.*(..))")
    public void pointCuts() {

    }

    @Around("pointCuts()")
    public Object arounds(ProceedingJoinPoint joinPoint) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Signature signature = joinPoint.getSignature();

        log.info(Thread.currentThread().getName()+"\t【注解】日志记录执行开始 ===== ");
        // 方法执行
        try {
            return joinPoint.proceed();
        } catch (Throwable e) {
            return e;
        } finally {
            stopWatch.stop();
            TimeHolder.setMethodTime(((MethodSignature) signature).getMethod().getName(), BigDecimal.valueOf(stopWatch.getTotalTimeSeconds()));
            // 方法执行后
            System.out.println(Thread.currentThread().getName()+"\t【注解】日志记录执行完毕 ===== ");
        }
    }

}

测试类

public static void main(String[] args) {
        AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = ioc.getBean(UserService.class);

        new Thread(()->{
            try {
                userService.creditApply();
                userService.creditQuery();
                userService.loanApply();
                ApiTimeMap threadApiTimeMap = TimeHolder.getThreadApiTimeMap();
                System.out.println(Thread.currentThread().getName()+"\t"+JSONUtils.toJSONString(threadApiTimeMap.getMap()));
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                TimeHolder.clear();
            }

        },"T1").start();


        new Thread(()->{
            try {
                userService.creditApply();
                userService.creditQuery();
                userService.loanApply();
                userService.loanQuery();
                ApiTimeMap threadApiTimeMap = TimeHolder.getThreadApiTimeMap();
                System.out.println(Thread.currentThread().getName()+"\t"+JSONUtils.toJSONString(threadApiTimeMap.getMap()));
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                TimeHolder.clear();
            }

        },"T2").start();
    }

最终实现的效果就是这样。

通过这样的方式,即便是其他程序员编写别的三方接口,也可以直接复用这部分功能,拓展性也是很好的。

总结

其实在平时的开发中,要多思考,那些是核心功能,或者通用功能,就需要在设计上多花点功夫进行设计,把不变的抽取出来,变化的进行隔离。 AOP 在很多场景可以使用。

  • 基于AOP 统计API接口耗时
  • 基于AOP 打印统一的日志
  • 基于AOP 事务管理
  • 基于AOP + 注解 实现接口幂等功能
  • 基于AOP + 注解 实现接口限流功能
  • 数据校验、缓存处理、异常处理等。
举报

相关推荐

0 条评论