2017-02-15 61 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; 
    } 
}