Spring MVC 의 비동기 모드(고성능 의 관건)

10673 단어 springmvc비동기
비동기 모드 가 무엇 입 니까?
비동기 모드 가 무엇 인지 알 고 싶 으 면 동기 모드 가 무엇 인지 먼저 알 고 가장 전형 적 인 동기 모드 를 먼저 봐 야 한다.

브 라 우 저 에서 요청 을 합 니 다.웹 서버 에서 스 레 드 처 리 를 하고 처리 결 과 를 브 라 우 저 로 되 돌려 줍 니 다.더 이상 할 말 이 없 는 것 같 습 니 다.대부분의 웹 서버 는 이렇게 처리 합 니 다.지금 생각해 보면 처리 과정 에서 백 엔 드 의 비 즈 니스 논리 서버 를 호출 해 야 한다 면 어떻게 될 까?

조정 하 세 요.위의 그림 에서 보 듯 이 요청 처리 스 레 드 는 Call 이 끝 난 후에 Return 을 기다 리 고 자신 이 차단 상태 에 있 습 니 다.이것 도 절대 다수의 웹 서버 의 방법 이다.일반적으로 이렇게 해도 충분 하 다.왜?첫째,'장시간 처리 서비스'호출 이 많 지 않 고,둘째,요청 수도 많 지 않다.그렇지 않 으 면 이런 모델 에 무슨 문제 가 생 길 까?문제 가 될 수 있 는 것 은 스 레 드 의 부족 을 처리 해 달라 고 요청 하 는 것 입 니 다!요청 처리 스 레 드 의 총 수 는 제한 되 어 있 기 때문에 유사 한 요청 이 많 으 면 모든 처리 스 레 드 가 막 힌 상태 에 있 으 면 새로운 요청 도 처리 할 수 없습니다.즉,서버 의 삼투 능력 에 영향 을 주 는 것 입 니 다.서버 의 모든 성능 을 더욱 잘 발휘 하려 면 이 보 를 사용 해 야 한 다 는 것 도 제목 에서 말 하 는'고성능 의 관건'이다.다음은 비동기 가 어떻게 된 일 인지 살 펴 보 자.

가장 큰 차이 점 은 스 레 드 처리 요청 이 배경 처리 에 대한 호출 에'invoke'방식 을 사용 한 것 입 니 다.즉,바 꾼 후에 바로 돌아 가 고 기다 리 지 않 으 면 스 레 드 처리 요청 이'자유'입 니 다.이 어 다른 요청 을 처리 할 수 있 습 니 다.백 엔 드 처리 가 완료 되면 리 셋 처리 스 레 드 를 연결 하여 호출 결 과 를 처리 할 수 있 습 니 다.이 리 셋 처리 스 레 드 와 요청 처리 스 레 드 는 스 레 드 탱크 의 특정한 스 레 드 일 수 있 습 니 다.서로 관계 가 없 을 수 있 습 니 다.이 리 셋 처리 스 레 드 에서 브 라 우 저 로 내용 을 되 돌려 줍 니 다.이것 이 바로 비동기 적 인 과정 이다.
가 져 온 개선 은 분명 하 다.요청 처리 스 레 드 는 막 을 필요 가 없다.그 능력 은 더욱 충분 하 게 사용 되 어 서버 의 삼투 능력 을 향상 시 켰 다.
Spring MVC 의 사용―Deffered Result
Spring MVC 의 비동기 기능 을 사용 하려 면 먼저 Servlet 3.0 이상 의 버 전 을 사용 하 는 지 확인 해 야 합 니 다.Maven 에서 이렇게 설정 합 니 다.

  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>3.1.0</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>4.2.3.RELEASE</version>
  </dependency>
제 가 사용 하 는 Servlet 버 전 은 3.1.0 이 고 Spring MVC 버 전 은 4.2.3 이 므 로 최신 버 전 을 사용 하 는 것 을 권장 합 니 다.
Spring MVC 의 양호 한 패 키 징 으로 인해 비동기 기능 은 사용 하기에 매우 간단 하 다.전통 적 인 동기 화 모드 의 Controller 는 Model AndView 로 돌아 가 고 비동기 모드 는 Deferred Result로 돌아 갑 니 다.
이 예 를 보면:

@RequestMapping(value="/asynctask", method = RequestMethod.GET)
public DeferredResult<ModelAndView> asyncTask(){
  DeferredResult<ModelAndView> deferredResult = new DeferredResult<ModelAndView>();
  System.out.println("/asynctask   !thread id is : " + Thread.currentThread().getId());
  longTimeAsyncCallService.makeRemoteCallAndUnknownWhenFinish(new LongTermTaskCallback() {
    @Override
    public void callback(Object result) {
      System.out.println("        , thread id is : " + Thread.currentThread().getId());
      ModelAndView mav = new ModelAndView("remotecalltask");
      mav.addObject("result", result);
      deferredResult.setResult(mav);
    }
  });
}

longtimeAsyncCallService 는 제 가 장시간 비동기 호출 을 모 의 하 는 서비스 클래스 입 니 다.호출 하면 바로 돌아 갑 니 다.처리 가 끝 났 을 때 스 레 드 를 연결 하여 우리 가 제공 하 는 리 셋 함 수 를 호출 합 니 다.이것 은'그림 3'설명 과 같이 코드 는 다음 과 같 습 니 다.

public interface LongTermTaskCallback {
  void callback(Object result);
}

public class LongTimeAsyncCallService {
  private final int CorePoolSize = 4;
  private final int NeedSeconds = 3;
  private Random random = new Random();
  private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(CorePoolSize);
  public void makeRemoteCallAndUnknownWhenFinish(LongTermTaskCallback callback){
    System.out.println("        : " + NeedSeconds + "  ");
    scheduler.schedule(new Runnable() {
      @Override
      public void run() {
        callback.callback("         .");
      }
    }, "      :)", TimeUnit.SECONDS);
  }
}
출력 결 과 는:
/synctask 호출!thread id is : 46
이 퀘 스 트 완성 시:3 초
비동기 호출 실행 완료,thread id is:47
결 과 를 되 돌려 주 는 스 레 드 와 요청 처리 스 레 드 가 같은 스 레 드 가 아 님 을 알 수 있 습 니 다.
WebAsyncTask 라 는 것 도 있어 요.
Deffered Result로 돌아 가 는 것 은 유일한 방법 이 아니 라 WebAsyncTask 로 돌아 가'비동기'를 실현 할 수 있 습 니 다.그러나 약간 다른 점 은 WebAsyncTask 로 돌아 가면 우리 가 자발적으로 Callback 을 호출 할 필요 가 없다 는 것 입 니 다.예 를 들 어:

@RequestMapping(value="/longtimetask", method = RequestMethod.GET)
public WebAsyncTask longTimeTask(){
  System.out.println("/longtimetask    thread id is : " + Thread.currentThread().getId());
  Callable<ModelAndView> callable = new Callable<ModelAndView>() {
    public ModelAndView call() throws Exception {
      Thread.sleep(3000); //          
      ModelAndView mav = new ModelAndView("longtimetask");
      mav.addObject("result", "    ");
      System.out.println("     thread id is : " + Thread.currentThread().getId());
      return mav;
    }
  };
  return new WebAsyncTask(callable);
}
그 핵심 은 Callable입 니 다.사실 Callable로 직접 돌아 가 는 것 도 가능 합 니 다.하지만 우 리 는 뒤에서 언급 한'시간 초과 처리'를 할 수 있 도록 포장 되 어 있 습 니 다.이전 방안 과 의 차 이 는 이 Callable 의 call 방법 이 우리 가 직접 호출 한 것 이 아니 라 longtimeTask 에서 돌아 온 후에 Spring MVC 가 하나의 작업 스 레 드 로 호출 하고 실행 하 며 인쇄 한 결과 입 니 다.
/longtimetask 호출 thread id is:56
실행 성공 thread id is:57
이 를 통 해 알 수 있 듯 이 서로 다른 스 레 드 에서 실 행 된 것 입 니 다.그러나 이 WebAsyncTask 는'그림 3'이 설명 한 기술 규격 에 부합 되 지 않 습 니 다.스 레 드 처 리 를 요청 하 는 임 무 를 다른 작업 스 레 드 에 간단하게 전달 하 는 것 일 뿐 입 니 다.
처리 시간 초과
만약 에'장시간 처리 임무'가 계속 돌아 오지 않 았 다 면 우 리 는 클 라 이언 트 를 무한 정 기다 리 게 해 서 는 안 된다.어쨌든'시간 초과'를 해 야 한다.그림:
 
사실'시간 초과 처리 스 레 드'와'리 셋 처리 스 레 드'는 모두 스 레 드 탱크 의 특정한 스 레 드 일 수 있 습 니 다.저 는 선명 하 게 구분 하기 위해 서 그 렸 을 뿐 입 니 다.이 시간 초과 처 리 를 추가 하 는 것 은 Spring MVC 에서 매우 간단 합 니 다.먼저 WebAsyncTask 코드 를 가지 고 변경 하 십시오.

@RequestMapping(value="/longtimetask", method = RequestMethod.GET)
public WebAsyncTask longTimeTask(){
  System.out.println("/longtimetask    thread id is : " + Thread.currentThread().getId());
  Callable<ModelAndView> callable = new Callable<ModelAndView>() {
    public ModelAndView call() throws Exception {
      Thread.sleep(3000); //          
      ModelAndView mav = new ModelAndView("longtimetask");
      mav.addObject("result", "    ");
      System.out.println("     thread id is : " + Thread.currentThread().getId());
      return mav;
    }
  };
  
  
  WebAsyncTask asyncTask = new WebAsyncTask(2000, callable);
  asyncTask.onTimeout(
      new Callable<ModelAndView>() {
        public ModelAndView call() throws Exception {
          ModelAndView mav = new ModelAndView("longtimetask");
          mav.addObject("result", "    ");
          System.out.println("     thread id is :" + Thread.currentThread().getId());
          return mav;
        }
      }
  );
  return new WebAsyncTask(3000, callable);
}

빨간색 글꼴 부분 코드 를 주의해 서 보 세 요.이것 이 바로 앞에서 언급 한 Callable 이 왜 한 층 을 아웃 소 싱 해 야 하 는 지 입 니 다.WebAsyncTask 에 시간 초과 반전 을 설정 하면 시간 초과 처 리 를 실현 할 수 있 습 니 다.이 예 에서 정상 적 인 처 리 는 3 초 이 고 시간 초과 설정 은 2 초 입 니 다.시간 초과 가 발생 할 수 있 습 니 다.인쇄 log 는 다음 과 같 습 니 다.
/longtimetask 호출 thread id is:59
실행 시간 초과 thread id is:61
실행 성공 thread id is:80
응?분명히 시간 을 초 과 했 는데 어떻게'집행 성공'을 할 수 있 습 니까?시간 을 초과 하면 시간 을 초과 하고 시간 을 초과 하면 정상 적 인 실행 절 차 를 중단 하지 않 습 니 다.그러나 시간 을 초과 한 후에 우 리 는 클 라 이언 트 에 게'시간 초과'결 과 를 되 돌려 주 었 습 니 다.그 다음 에 정상 적 인 처리 절차 가 성공 하 더 라 도 클 라 이언 트 는 정상 적 인 처리 성공 으로 인해 발생 하 는 결 과 를 받 지 못 합 니 다.이 로 인해 발생 하 는 문 제 는 클 라 이언 트 가'시간 초과'를 보 았 고 실제 작업 이 성공 한 것 입 니까?클 라 이언 트 는 모 르 지만 보통 이것 도 큰 문제 가 아 닙 니 다.사용자 가 브 라 우 저 에서 다시 갱신 하면 되 기 때 문 입 니 다.:D
자,Deffered Result 방식 의 시간 초과 처 리 를 살 펴 보 겠 습 니 다.
 

  @RequestMapping(value = "/asynctask", method = RequestMethod.GET)
  public DeferredResult<ModelAndView> asyncTask() {
    DeferredResult<ModelAndView> deferredResult = new DeferredResult<ModelAndView>(2000L);
    System.out.println("/asynctask   !thread id is : " + Thread.currentThread().getId());
    longTimeAsyncCallService.makeRemoteCallAndUnknownWhenFinish(new LongTermTaskCallback() {
      @Override
      public void callback(Object result) {
        System.out.println("        , thread id is : " + Thread.currentThread().getId());
        ModelAndView mav = new ModelAndView("remotecalltask");
        mav.addObject("result", result);
        deferredResult.setResult(mav);
      }
    });

    deferredResult.onTimeout(new Runnable() {
      @Override
      public void run() {
        System.out.println("        !thread id is : " + Thread.currentThread().getId());
        ModelAndView mav = new ModelAndView("remotecalltask");
        mav.addObject("result", "        ");
        deferredResult.setResult(mav);
      }
    });

    return deferredResult;
  }

매우 유사 합 니 다.그 렇 죠?저 는 시간 초 과 를 2 초 로 설정 하고 정상 적 인 처 리 는 3 초 걸 립 니 다.반드시 시간 초 과 를 할 것 입 니 다.실행 결 과 는 다음 과 같 습 니 다.
/synctask 호출!thread id is : 48
이 퀘 스 트 완성 시:3 초
비동기 호출 실행 시간 초과!thread id is : 51
비동기 호출 실행 완료,thread id is:49
완전히 우리 가 예 상 했 던 것 이다.
예외 처리
별 차이 가 없 는 것 같 습 니 다.Controller 에서 의 처 리 는 이전 동기 화 모드 의 처리 와 같 습 니 다.

  @ExceptionHandler(Exception.class)
  public ModelAndView handleAllException(Exception ex) {
    ModelAndView model = new ModelAndView("error");
    model.addObject("result", ex.getMessage());
    return model;
  }
또 전체적인 이상 처 리 를 해 야 한다.과거의 방법 과 마찬가지 로 여기 서 는 표현 하지 않 는 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기