【工具】方法日誌列印+任務切片

貓咪大王_lkb發表於2021-04-03

@

方法日誌列印

最近開發遇到的一個問題是關於日誌列印。日誌非常重要,通過檢視日誌,我們可以快速排查問題。
我們專案的整個系統內閘道器處做了攔截,能夠看到前端傳過來的日誌,但是內部服務間呼叫,服務間的入參與出參看不到;還有,一些重要的方法如果要看到入參和出參,只能一個個去寫log方法。
這種重複的工作非常適合使用切面來寫一些小工具。下面是我寫的小工具,下面方法幾乎是搬運一位老哥寫的部落格,老哥的部落格找不見了,sorry。

定義註釋類

/**
 * @Description 列印引數
 * @Author lkb
 * @CreateDate: 2021/3/25
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MethodLog {
}

AOP 列印輸入輸出引數

/**
 * @Description
 * @Author lkb
 * @CreateDate: 2021/3/25
 */
@Aspect
@Component
@Slf4j
public class MethodLogAspect {

    @Pointcut("@annotation(com.....annotion.MethodLog)")
    public void pointCut(){}

    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        try{
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            MethodLog annotation = method.getAnnotation(MethodLog.class);
            if (Objects.isNull(annotation)){
                try {
                    return joinPoint.proceed();
                } catch (Throwable throwable) {
                    log.error("method proceed fail. e = {}", throwable.getMessage(), throwable);
                    throw throwable;
                }
            }else{
                String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();
                log.info("methodName: "+methodName);
                Object[] args = joinPoint.getArgs();
                log.info("param: " + JsonUtil.toJSON(args));

                Instant start = Instant.now();
                log.info("method start. methodName = {}", methodName);
                Object result = null;
                try {
                    // 這裡的result記得return
                    result = joinPoint.proceed();
                } catch (Throwable throwable) {
                    log.error("method proceed fail. e = {}", throwable.getMessage(), throwable);
                    throw throwable;
                }

                log.info("method end. methodName = {}, cost = {}", methodName, ChronoUnit.MILLIS.between(start, Instant.now()));

                return result;
            }
        }catch (Exception e){
            log.error("MethodLogAspect around fail. e= {}", e.getMessage(), e);
            throw e;
        }
    }
}

任務切片

遇到的第二個問題是,
在一個呼叫中,需要多次呼叫外部系統的服務,本來應該使用MQ去解耦,但是因為多方面原因只能維持現狀(我們服務多次呼叫外部系統的服務)。這樣導致整個請求特別慢,上線多臺裝置的時候可能需要十幾二十秒。
與其他兩個系統的互動互不影響,相互獨立。因此,我想到使用任務切片的方式來加快執行速度。
下面是我寫的任務切片小工具。

DTO

/**
 * @Description
 * @Author lkb
 * @CreateDate: 2021/3/26
 */
@Data
public class AsynTaskDTO<T, R> {

    public T param;

    public Function<T, R> function;

    public R resultData;

    public Boolean result;

    public String key;

    public AsynTaskDTO(Function<T,R> function, T param){
        this.function = function;
        this.param = param;
        this.result = false;
        this.key = String.valueOf(param.hashCode());
    }

}

任務切片實現類

/**
 * @Description
 * @Author lkb
 * @CreateDate: 2021/3/26
 */
@Slf4j
@Getter
public class AsynTaskUtil {

    private static final ForkJoinPool ASYNC_IO_POOL = new ForkJoinPool((Runtime.getRuntime().availableProcessors() << 1) + 1);

    private List<AsynTaskDTO> taskDTOS;

    public AsynTaskUtil(List<AsynTaskDTO> taskDTOS){
        this.taskDTOS = taskDTOS;
    }

    public boolean doTask(){
        CompletableFuture[] futures = taskDTOS.stream().map(task -> CompletableFuture.runAsync(() -> {
            try{
                task.setResultData(task.getFunction().apply(task.getParam()));
                task.setResult(true);
            }catch (Exception e){
                log.error("AsynTaskUtil doTask fail. e = {}" + e.getMessage(), e);
                task.setResult(false);
            }
        }, ASYNC_IO_POOL)).toArray(CompletableFuture[]::new);

        CompletableFuture.allOf(futures).join();

        boolean f = taskDTOS.stream().anyMatch(t -> Objects.equals(false, t.getResult()));
        return !f;
    }
}

相關文章