簡體   English   中英

Spring Boot DeferredResult Aspect使CPU飛速發展

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM