Spring Boot DeferredResult Aspect使CPU变得很高

时间:2017-02-15 09:01:34

标签: spring spring-boot aop aspect

我们正在使用Spring Boot来开发我们的服务。我们选择以异步方式执行此操作,并且遇到以下问题:我们在所有异步休息资源之上有以下方面:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.context.request.async.DeferredResult;


@Aspect
@Component
public class TerminatedUsersAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger("[TERMINATED_USERS]");
    public static final ThreadLocal<String> ID_LOCAL = new ThreadLocal<>();

    @Autowired
    private UserRepository userRepo;

    @Autowired
    private UserService userService;

    @Autowired
    private ExecutorService executorService;

    @SuppressWarnings("unchecked")
    @Around("within(com.test..*) && @annotation(authorization)")
    public Object checkForId(ProceedingJoinPoint joinPoint, Authorization authorization) throws Throwable {

        final MethodInvocationProceedingJoinPoint mJoinPoint = (MethodInvocationProceedingJoinPoint) joinPoint;
        final MethodSignature signature = (MethodSignature) mJoinPoint.getSignature();
        final DeferredResult<Object> ret = new DeferredResult<>(60000L);
        final String id = ID_LOCAL.get();

        if (signature.getReturnType().isAssignableFrom(DeferredResult.class) && (id != null)) {

            userRepo.getAccountStatus(id).thenAcceptAsync(status -> {
                boolean accountValid = userService.isAccountValid(status, true);

                if (!accountValid) {
                    LOGGER.debug("AccountId: {} is not valid. Rejecting with 403", id);
                    ret.setErrorResult(new ResponseEntity<String>("Invalid account.", HttpStatus.FORBIDDEN));
                    return;
                }

                try {

                    final DeferredResult<Object> funcRet = (DeferredResult<Object>) joinPoint.proceed();

                    funcRet.setResultHandler(r -> {
                        ret.setResult(r);
                    });

                    funcRet.onTimeout(() -> {
                        ret.setResult(new AsyncRequestTimeoutException());
                    });

                } catch (Throwable e) {
                    ret.setErrorResult(ret);
                }

            }, executorService).exceptionally(ex -> {
                ret.setErrorResult(ex);
                return null;
            });

            return ret;
        }

        return joinPoint.proceed();
    }

}

我们的应用程序中的嵌入式服务器正在进行中。问题出现了。似乎在经过将近一天之后,由于这方面的原因,CPU最终会飙升100%的红色。我调试了代码,从我的角度看似乎很好,但也许我错过了什么? 任何想法都会受到欢迎。谢谢, C.

2 个答案:

答案 0 :(得分:1)

首先让我感到震惊的是你在代码中调用了两次joinPoint.proceed()。第一个是将它转换为DeferredResult,第二个是从方法返回时。你应该重写它,这样它只会被执行一次。您可以考虑将@Around注释用于执行&#39; (https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-pointcuts-examples)而不是&#39;因为它允许您指定返回类型。在这种情况下,如果基于方法返回类型,您将不再需要它。

说完了,我不确定这是否能解决你的问题。我在那里缺乏背景,我不知道你想要达到的目标。可能有一种更简单的方法,因为在该上下文中使用延迟结果对我来说有点奇怪。此外,特别是导致100%资源使用的原因是什么?是否有一些线程无限运行或某些HTTP连接挂起?

答案 1 :(得分:0)

这个小重构怎么样?它是类型安全的,不需要反射,只需调用filename="" while [[ $filename = "" ]];do echo "Enter the file name" read filename if [[ -f $filename ]];then echo "$filename exists" else echo "File name doesn't exists, so re-enter" fi done 一次。

此外,我认为在您的代码中proceed()没有多大意义,我将其替换为ret.setErrorResult(ret),将实际的执行设置为错误结果。

我也希望你能原谅我重命名一些变数。它帮助我更好地了解发生了什么。我仍然不是百分百肯定,因为我对Spring一无所知(但很多关于AspectJ)。

setErrorResult(e)