[英]Spring Boot DeferredResult Aspect makes CPU go sky high
我們正在使用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。
讓我大吃一驚的是,您在代碼中兩次調用了joinPoint.proceed()。 第一個當您將其強制轉換為DeferredResult時,第二個當您從方法返回時。 您應該重寫它,使其僅執行一次。 您可以考慮將@Around注釋與“執行”( https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-pointcuts-examples )一起使用,而不是將“內”,因為它允許您指定返回類型。 在這種情況下,如果基於方法返回類型,則不需要該方法。
話雖如此,我不確定這是否可以解決您的問題。 我在那里沒有背景,我不知道你要達到什么目標。 可能有一種更簡單的方法,因為在這種情況下使用延遲結果對我來說有點奇怪。 此外,是什么原因導致100%的資源使用? 是否有一些線程無限運行或某些HTTP連接掛起?
這個小重構怎么樣? 它是類型安全的,不需要反射,只調用一次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;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.