2017-02-15 60 views
0

我们正在使用Spring引导来发展我们servies。我们选择做在一个异步的方式,我们都面临着以下问题:我们有以下方面对我们所有的异步其余资源的顶部:春季启动DeferredResult方面使得CPU去比天高

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.

回答

1

令我惊讶的是,你在你的代码中调用joinPoint.proceed()两次第一件事。第一个是将它转换为DeferredResult时,第二个是从方法返回时。你应该重写它,所以它只会被执行一次。您可以考虑使用@Around批注与,而不是“内”,因为它允许你指定的返回类型的“执行”(https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-pointcuts-examples)。在这种情况下,如果基于方法返回类型,则不需要这样做。

具有说,我不知道这是否会解决您的问题。我在那里缺乏背景,我不知道你在努力实现什么。可能有一个更简单的方法来做到这一点,因为在这种情况下对延迟结果的使用对我来说有点奇怪。此外,特别是造成100%的资源使用情况?那里有一些无限的线程或者一些HTTP连接挂了吗?

+0

我同意everyathing在这里说。你似乎太宽泛地应用了这个方面,大的“if”和所有昂贵的反射材料都没有必要。那么'if()'后面的'proceed()'首先是不必要的,但是如果你使用它,至少把它放到'else'分支中。我会为你准备一些小样本。 – kriegaex

0

怎么样这个小重构?它是类型安全的,不需要反射,只需要调用proceed()一次。

此外,我认为在你的代码ret.setErrorResult(ret)没有太大意义,我把它替换为setErrorResult(e),将实际执行设置为错误结果。

我也希望你能原谅我改名一些变量。它帮助我更好地理解发生了什么。我仍然不是100%确定的,因为我对Spring一无所知(但很多关于AspectJ的知识)。

package de.scrum_master.aspect; 

import java.util.concurrent.ExecutorService; 

import org.aspectj.lang.ProceedingJoinPoint; 
import org.aspectj.lang.annotation.Around; 
import org.aspectj.lang.annotation.Aspect; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
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; 

    @Around(
    "within(com.test..*) && @annotation(authorization) && " + 
    "execution(org.springframework.web.context.request.async.DeferredResult *(..))" 
) 
    public DeferredResult<Object> checkForId(ProceedingJoinPoint joinPoint, Authorization authorization) throws Throwable { 

    final DeferredResult<Object> aspectResult = new DeferredResult<>(60000L); 
    final String id = ID_LOCAL.get(); 

    userRepo 
     .getAccountStatus(id) 
     .thenAcceptAsync(status -> { 
      boolean accountValid = userService.isAccountValid(status, true); 
      if (!accountValid) { 
      LOGGER.debug("AccountId: {} is not valid. Rejecting with 403", id); 
      aspectResult.setErrorResult(new ResponseEntity<String>("Invalid account.", HttpStatus.FORBIDDEN)); 
      return; 
      } 
      try { 
      @SuppressWarnings("unchecked") 
      final DeferredResult<Object> originalResult = (DeferredResult<Object>) joinPoint.proceed(); 
      originalResult.setResultHandler(r -> { aspectResult.setResult(r); }); 
      originalResult.onTimeout(() -> { aspectResult.setResult(new AsyncRequestTimeoutException()); }); 
      } catch (Throwable e) { 
      aspectResult.setErrorResult(e); 
      } 
     }, 
     executorService 
    ) 
     .exceptionally(ex -> { 
      aspectResult.setErrorResult(ex); 
      return null; 
     } 
    ); 

    return aspectResult; 
    } 
}