Spring-MVC 비동기 요청 Servlet 비동기 처리

Servlet 3.0 의 규범 에 비동기 요청 에 대한 지원 이 추가 되 었 고 SpringMVC 는 이 를 바탕 으로 비동기 요청 에 편 의 를 제공 했다.
비동기 요청 은 비교적 오래 걸 리 는 업 무 를 처리 할 때 먼저 request 를 되 돌려 준 다음 에 다른 스 레 드 로 시간 이 걸 리 는 업 무 를 처리 하고 처리 한 후에 사용자 에 게 되 돌려 주 는 것 입 니 다.
비동기 요청 은 우리 에 게 많은 편 의 를 가 져 다 줄 수 있 습 니 다.가장 직접적인 용법 은 바로 시간 이 걸 리 는 업 무 를 처리 하 는 것 입 니 다.예 를 들 어 데이터 베 이 스 를 조회 해 야 하고 다른 서버 로 처리 해 야 하 는 등 상황 에서 먼저 요청 을 클 라 이언 트 에 게 되 돌려 준 다음 에 새로운 스 레 드 처리 시간 이 걸 리 는 업 무 를 사용 할 수 있 습 니 다.
만약 에 우리 가 적당 한 확장 을 하면 구독 자 모드 의 정보 구독 기능 을 실현 할 수 있다.예 를 들 어 이상 상황 이 발생 할 때 관련 정 보 를 운영 자 에 게 자발적으로 보 낼 수 있 고 현재 의 많은 메 일 자동 답장 은 모두 이런 기술 을 사용한다.
Http 프로 토 콜 은 단 방향 입 니 다.클 라 이언 트 만 당 길 수 있 고 서버 가 주동 적 으로 밀 수 없습니다.Servlet 이 비동기 요청 에 대한 지원 은 Http 를 수정 하지 않 고 Http 에 대한 교묘 한 이용 입 니 다.비동기 요청 의 핵심 원 리 는 주로 두 가지 로 나 뉘 는데 하 나 는 폴 링 이 고 다른 하 나 는 긴 연결 이다.
폴 링 은 돌아 올 데이터 가 있 는 지 없 는 지 를 정기 적 으로 자동 으로 요청 하 는 것 으로 자원 에 대한 낭비 가 비교적 크다.긴 연결 의 원 리 는 클 라 이언 트 가 요청 을 하 는 것 입 니 다.서버 에서 처리 하고 돌아 온 후에 연결 을 끝내 지 않 으 면 뒤에서 클 라 이언 트 데 이 터 를 다시 되 돌려 줄 수 있 습 니 다.
Servlet 이 비동기 요청 에 대한 지원 은 사실 긴 연결 방식 을 사용 합 니 다.즉,비동기 요청 에서 원시 적 인 요청 이 돌아 올 때 연결 을 닫 지 않 았 습 니 다.닫 힌 것 은 요청 을 처리 한 그 현성 일 뿐 비동기 요청 이 모두 처 리 된 후에 만 연결 을 닫 을 수 있 습 니 다.
Servlet 3.0 비동기 요청 지원
Servlet 3.0 규범 에서 비동기 처리 요청 을 사용 하 는 것 은 매우 간단 합 니 다.요청 처리 과정 에서 request 의 startAsync 를 호출 하여 AsyncContext 로 돌아 가 야 합 니 다.
AsyncContext 가 비동기 요청 에서 매우 중요 한 역할 을 하 는 것 은 비동기 요청 컨 텍스트 라 고도 할 수 있 고 비동기 요청 용기 라 고도 할 수 있 습 니 다.ServletContext 와 유사 합 니 다.startAsync 를 여러 번 호출 했 는데 모두 같은 AsyncContext 로 되 돌 아 왔 습 니 다.코드 는 다음 과 같 습 니 다:

public interface AsyncContext {
  String ASYNC_REQUEST_URI = "javax.servlet.async.request_uri";
  String ASYNC_CONTEXT_PATH = "javax.servlet.async.context_path";
  String ASYNC_PATH_INFO = "javax.servlet.async.path_info";
  String ASYNC_SERVLET_PATH = "javax.servlet.async.servlet_path";
  String ASYNC_QUERY_STRING = "javax.servlet.async.query_string";
  ServletRequest getRequest();
  ServletResponse getResponse();
  boolean hasOriginalRequestAndResponse();
  void dispatch();
  void dispatch(String var1);
  void dispatch(ServletContext var1, String var2);
  void complete();
  void start(Runnable var1);
  void addListener(AsyncListener var1);
  void addListener(AsyncListener var1, ServletRequest var2, ServletResponse var3);
  <T extends AsyncListener> T createListener(Class<T> var1) throws ServletException;
  void setTimeout(long var1);
  long getTimeout();
}
getResponse()는 response 를 가 져 오 는 데 사 용 됩 니 다.dispatch 는 새 주 소 를 나 누 어 주 는 데 사 용 됩 니 다.complete 는 용기 가 처리 되 었 음 을 알 리 는 데 사 용 됩 니 다.start 방법 은 실제 처리 스 레 드 를 시작 하 는 데 사 용 됩 니 다.addListener 는 모니터 를 추가 하 는 데 사 용 됩 니 다.setTimeout 방법 은 시간 초과 수정 에 사 용 됩 니 다.
Servlet 3.0 비동기 요청 인 스 턴 스 처리

@WebServlet(
name = “WorkServlet”,
urlPatterns = “/work”,
asyncSupported = true
)
public class WorkServlet extends HttpServlet {
private static final long serialVersionUID =1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  //  ContentType,    
  resp.setContentType("text/plain;charset=UTF-8");
  resp.setHeader("Cache-Control","private");
  resp.setHeader("Pragma","no-cache");
  final PrintWriter writer= resp.getWriter();
  writer.println("       ");
  writer.flush();
  List<String> zuoyes=new ArrayList<String>();
  for (int i = 0; i < 10; i++) {
    zuoyes.add("zuoye"+i);;
  }
  final AsyncContext ac=req.startAsync();//      
  doZuoye(ac,zuoyes);
  writer.println("      ");
  writer.flush();
}

private void doZuoye(final AsyncContext ac, final List<String> zuoyes) {
  ac.setTimeout(1*60*60*1000L);
  ac.start(new Runnable() {
    @Override
    public void run() {
      //  response       
      try {
        PrintWriter writer=ac.getResponse().getWriter();
        for (String zuoye:zuoyes) {
          writer.println("\""+zuoye+"\"     ");
          Thread.sleep(1*1000L);
          writer.flush();
        }
        ac.complete();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  });
}

비동기 요청 모니터
위의 절 차 는 우리 의 가장 기본 적 인 비동기 요구 이지 만 완선 되 지 않다.선생님 은 거시적인 문 제 를 생각해 야 하기 때문에 숙제 를 다 한 후에 선생님 께 어떤 문제 가 어렵 고 어떤 문제 가 문제 가 있 거나 자신의 이번 경험 을 정리 해 야 합 니까?그러나 이런 일 들 은 숙제 를 하 는 학생 이 해 서 는 안 되 고 전문 적 인 학습 보고 원 이 통계 적 으로 분석 해 야 합 니 다.그래서 모니터 가 생 겼 어 요.

public class TeacherListener implements AsyncListener {
  final SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  @Override
  public void onComplete(AsyncEvent event) throws IOException {
    System.out.println(" "+formatter.format(new Date())+"      ");
  }

  @Override
  public void onTimeout(AsyncEvent event) throws IOException {
    System.out.println(" "+formatter.format(new Date())+"    ");
  }

  @Override
  public void onError(AsyncEvent event) throws IOException {
    System.out.println(" "+formatter.format(new Date())+"      ");
  }

  @Override
  public void onStartAsync(AsyncEvent event) throws IOException {
    System.out.println(" "+formatter.format(new Date())+"      ");
  }
}
모든 코드 는 github 주 소 를 참조 합 니 다.
https://github.com/lzggsimida123/ServletAsync
추가:SpringMVC 가 Servlet 3 비동기 요청 에 대한 지원
SpringMVC 가 Servlet 3 비동기 요청 에 대한 지원 은 프로세서 방법 을 통 해 Callable 과 Deferred Result 를 되 돌려 주 는 두 가지 방식 이 있 습 니 다.
Servlet 3 의 규범 에 따라 비동기 요청 을 지원 할 때 대응 하 는 Servlet 과 Filter 를 설정 하여 비동기 요청 을 지원 해 야 합 니 다.SpringMVC 가 비동기 요청 처 리 를 지원 하도록 하려 면 Dispatcher Servlet 을 정의 할 때 비동기 요청 을 지원 하도록 설정 해 야 합 니 다.Dispatcher Servlet 이전에 정 의 된 Filter 도 비동기 요청 을 지원 하도록 설정 해 야 합 니 다.

<servlet>
  <servlet-name>springmvc</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext-mvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
  <!--        -->
  <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
  <servlet-name>springmvc</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>
다시 호출 가능
프로세서 의 반환 방법 이 Callable 형식 일 때 기본적으로 비동기 요청 을 하고 TaskExecutor 를 사용 하여 되 돌아 오 는 Callable 을 호출 합 니 다.이후 처 리 는 정상 적 인 SpringMVC 요청 과 같 습 니 다.Callable 의 반환 결과 도 정상적으로 SpringMVC 를 요청 한 것 과 마찬가지 로 Model,Model AndView,String,Object 등 을 되 돌려 주 고@Response Body 와 결합 해 사용 할 수 있 으 며,구체 적 으로 Callable MethodReturnValueHandler 의 handle ReturnValue()를 참고 할 수 있다.

  @RequestMapping("/callable")
  public Callable<String> forCallable(Model model) throws Exception {
    return () -> {
      TimeUnit.SECONDS.sleep(1);//  1 ,        
      model.addAttribute("a", "aaaaaaa");
      return "async_request_callable";
    };
  }
하나의 Callable 요청 에 대해 시간 초과 시간 을 지정 해 야 한다 면,우 리 는 Callable 을 WebAsyncTask 로 감 쌀 수 있 습 니 다.그리고 시간 초과 반전 과 정상 적 인 처리 가 완 료 된 반전 도 지정 할 수 있다.

 @RequestMapping("/callable/timeout")
  public WebAsyncTask<String> forCallableWithTimeout(Model model) throws Exception {
    long timeout = 5 * 1000L;
    WebAsyncTask<String> asyncTask = new WebAsyncTask<>(timeout, () -> {
      TimeUnit.MILLISECONDS.sleep(timeout + 10);
      model.addAttribute("a", "aaaaaaa");
      return "async_request_callable";
    });
    asyncTask.onTimeout(() -> {
      System.out.println("      ");
      return "async_request_callable_timeout";
    });
    asyncTask.onCompletion(() -> {
      System.out.println("  callable       ");
    });
    return asyncTask;
  }
이 연 결 된 결 과 를 되 돌려 줍 니 다.
Deferred Result 를 사용 하여 결 과 를 되 돌려 주 는 프로 그래 밍 은 보통 프로세서 방법 에서 Deferred Result 인 스 턴 스 를 만 들 고 저장 한 후에 되 돌려 줍 니 다.예 를 들 어 하나의 대기 열 에 저장 하 는 것 입 니 다.그리고 다른 스 레 드 에서 이 대기 열 에서 해당 하 는 Deferred Result 대상 을 받 아 해당 하 는 업무 처 리 를 한 후 Deferred Result 에 해당 하 는 반환 값 을 설정 합 니 다.Deferred Result 를 되 돌려 준 후 SpringMVC 는 Deferred ResultHandler 를 만들어 Deferred Result 를 감청 하 는 데 사용 합 니 다.Deferred Result 에 반환 값 이 설정 되면 Deferred ResultHandler 는 반환 값 을 처리 합 니 다.Deferred Result 의 처리 과정 은 Deferred ResultMethodReturnValueHandler 의 handle ReturnValue()를 보십시오.

@RequestMapping("/deferredresult")
public DeferredResult<String> forDeferredResult() throws Exception {
  DeferredResult<String> result = new DeferredResult<>();
  new Thread(() -> {
    try {
      TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    result.setResult("async_request_deferredresult");
  }).start();
  return result;
}
Deferred Result 도 시간 초과 와 시간 초과 후의 리 셋 을 단독으로 지정 할 수 있 습 니 다.시간 초과 시간 은 구조 함 수 를 통 해 직접 전달 할 수 있 고 단 위 는 밀리초 입 니 다.

@RequestMapping("/deferredresult/timeout")
public DeferredResult<String> forDeferredResultWithTimeout() throws Exception {
  DeferredResult<String> result = new DeferredResult<>(10 * 1000);
  new Thread(() -> {
    try {
      TimeUnit.SECONDS.sleep(31);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    result.setResult("async_request_deferredresult");
  }).start();
  
  result.onTimeout(() -> {
    System.out.println("        ");
  });
  
  result.onCompletion(() -> {
    System.out.println("         ");
  });
  
  return result;
}
배치 하 다.
의 하위 요소를 통 해 비동기 요청 의 기본 시간 초과 와 사용 할 TaskExecutor 를 정의 할 수 있 습 니 다.기본 시간 초과 가 지정 되 지 않 으 면 기본적으로 용기 의 비동기 요청 시간 초 과 를 사용 합 니 다.사용 할 TaskExecutor 가 지정 되 지 않 으 면 Simple AsyncTaskExecutor 를 사용 합 니 다.아래 설정 에 서 는 기본 시간 초과 가 15 초 이 며,비동기 요청 을 처리 하 는 TaskExecutor 는 bean 용기 에 있 는 asyncTaskExecutor 라 는 TaskExecutor 를 설정 합 니 다.

<mvc:annotation-driven>
  <mvc:async-support default-timeout="15000" task-executor="asyncTaskExecutor"/>
</mvc:annotation-driven>
차단기
Callable 형식의 요청 을 되 돌려 주 는 것 은 Callable Processing Interceptor 인 터 페 이 스 를 통 해 차단 기 를 사용자 정의 할 수 있 고,Callable Processing Interceptor Adapter 추상 류 를 계승 하여 차단 기 를 정의 할 수 있 으 며,관심 있 는 방법 만 선택 하여 실현 할 수 있 습 니 다.CallableProcessingInterceptor 인터페이스 정 의 는 다음 과 같 습 니 다.

public interface CallableProcessingInterceptor { 
	static final Object RESULT_NONE = new Object(); 
	static final Object RESPONSE_HANDLED = new Object();
 
	/**
	 * Invoked <em>before</em> the start of concurrent handling in the original
	 * thread in which the {@code Callable} is submitted for concurrent handling.
	 *
	 * <p>
	 * This is useful for capturing the state of the current thread just prior to
	 * invoking the {@link Callable}. Once the state is captured, it can then be
	 * transferred to the new {@link Thread} in
	 * {@link #preProcess(NativeWebRequest, Callable)}. Capturing the state of
	 * Spring Security's SecurityContextHolder and migrating it to the new Thread
	 * is a concrete example of where this is useful.
	 * </p>
	 *
	 * @param request the current request
	 * @param task the task for the current async request
	 * @throws Exception in case of errors
	 */
	<T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) throws Exception;
 
	/**
	 * Invoked <em>after</em> the start of concurrent handling in the async
	 * thread in which the {@code Callable} is executed and <em>before</em> the
	 * actual invocation of the {@code Callable}.
	 *
	 * @param request the current request
	 * @param task the task for the current async request
	 * @throws Exception in case of errors
	 */
	<T> void preProcess(NativeWebRequest request, Callable<T> task) throws Exception;
 
	/**
	 * Invoked <em>after</em> the {@code Callable} has produced a result in the
	 * async thread in which the {@code Callable} is executed. This method may
	 * be invoked later than {@code afterTimeout} or {@code afterCompletion}
	 * depending on when the {@code Callable} finishes processing.
	 *
	 * @param request the current request
	 * @param task the task for the current async request
	 * @param concurrentResult the result of concurrent processing, which could
	 * be a {@link Throwable} if the {@code Callable} raised an exception
	 * @throws Exception in case of errors
	 */
	<T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) throws Exception;
 
	/**
	 * Invoked from a container thread when the async request times out before
	 * the {@code Callable} task completes. Implementations may return a value,
	 * including an {@link Exception}, to use instead of the value the
	 * {@link Callable} did not return in time.
	 *
	 * @param request the current request
	 * @param task the task for the current async request
	 * @return a concurrent result value; if the value is anything other than
	 * {@link #RESULT_NONE} or {@link #RESPONSE_HANDLED}, concurrent processing
	 * is resumed and subsequent interceptors are not invoked
	 * @throws Exception in case of errors
	 */
	<T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception;
 
	/**
	 * Invoked from a container thread when async processing completes for any
	 * reason including timeout or network error.
	 *
	 * @param request the current request
	 * @param task the task for the current async request
	 * @throws Exception in case of errors
	 */
	<T> void afterCompletion(NativeWebRequest request, Callable<T> task) throws Exception;
 
}
그것 의 설정 은를 통 해 설정 되 었 습 니 다.

<mvc:annotation-driven>
  <mvc:async-support default-timeout="15000" task-executor="asyncTaskExecutor">
    <mvc:callable-interceptors>
      <bean class="YourCallableProcessingInterceptor"/>
    </mvc:callable-interceptors>
  </mvc:async-support>
</mvc:annotation-driven>
Deferred Result 로 돌아 가 는 것 도 차단 할 수 있 습 니 다.이 는 Deferred ResultProcessing Interceptor 인 터 페 이 스 를 실현 하거나 Deferred ResultProcessing Interceptor Adapter 를 계승 해 야 합 니 다.DeferredResultProcessingInterceptor 인터페이스 정 의 는 다음 과 같 습 니 다.

public interface DeferredResultProcessingInterceptor {
 
	/**
	 * Invoked immediately before the start of concurrent handling, in the same
	 * thread that started it. This method may be used to capture state just prior
	 * to the start of concurrent processing with the given {@code DeferredResult}.
	 *
	 * @param request the current request
	 * @param deferredResult the DeferredResult for the current request
	 * @throws Exception in case of errors
	 */
	<T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception;
 
	/**
	 * Invoked immediately after the start of concurrent handling, in the same
	 * thread that started it. This method may be used to detect the start of
	 * concurrent processing with the given {@code DeferredResult}.
	 *
	 * <p>The {@code DeferredResult} may have already been set, for example at
	 * the time of its creation or by another thread.
	 *
	 * @param request the current request
	 * @param deferredResult the DeferredResult for the current request
	 * @throws Exception in case of errors
	 */
	<T> void preProcess(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception;
 
	/**
	 * Invoked after a {@code DeferredResult} has been set, via
	 * {@link DeferredResult#setResult(Object)} or
	 * {@link DeferredResult#setErrorResult(Object)}, and is also ready to
	 * handle the concurrent result.
	 *
	 * <p>This method may also be invoked after a timeout when the
	 * {@code DeferredResult} was created with a constructor accepting a default
	 * timeout result.
	 *
	 * @param request the current request
	 * @param deferredResult the DeferredResult for the current request
	 * @param concurrentResult the result to which the {@code DeferredResult}
	 * @throws Exception in case of errors
	 */
	<T> void postProcess(NativeWebRequest request, DeferredResult<T> deferredResult, Object concurrentResult) throws Exception;
 
	/**
	 * Invoked from a container thread when an async request times out before
	 * the {@code DeferredResult} has been set. Implementations may invoke
	 * {@link DeferredResult#setResult(Object) setResult} or
	 * {@link DeferredResult#setErrorResult(Object) setErrorResult} to resume processing.
	 *
	 * @param request the current request
	 * @param deferredResult the DeferredResult for the current request; if the
	 * {@code DeferredResult} is set, then concurrent processing is resumed and
	 * subsequent interceptors are not invoked
	 * @return {@code true} if processing should continue, or {@code false} if
	 * other interceptors should not be invoked
	 * @throws Exception in case of errors
	 */
	<T> boolean handleTimeout(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception;
 
	/**
	 * Invoked from a container thread when an async request completed for any
	 * reason including timeout and network error. This method is useful for
	 * detecting that a {@code DeferredResult} instance is no longer usable.
	 *
	 * @param request the current request
	 * @param deferredResult the DeferredResult for the current request
	 * @throws Exception in case of errors
	 */
	<T> void afterCompletion(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception;
 
}
사용자 정의 Deferred ResultProcessing Interceptor 는를 통 해 설정 되 었 습 니 다.

<mvc:annotation-driven>
  <mvc:async-support default-timeout="15000" task-executor="asyncTaskExecutor">
    <mvc:deferred-result-interceptors>
      <bean class="YourDeferredResultProcessingInterceptor"/>
    </mvc:deferred-result-interceptors>
  </mvc:async-support>
</mvc:annotation-driven>
비동기 요청 을 할 때 SpringMVC 의 전통 적 인 Handler Interceptor 의 post Handle()과 after Complete()는 실행 되 지 않 지만 비동기 요청 이 끝나 면 실 행 됩 니 다.비동기 처리 가 끝 난 후에 뭔 가 를 해 야 한다 면 AsyncHandler Interceptor 인 터 페 이 스 를 실현 하 는 after Concurrent Handling Started(),AsyncHandler Interceptor 인 터 페 이 스 를 선택 하여 Handler Interceptor 를 계승 할 수 있 습 니 다.
(주:본 고 는 Spring 4.1.0 을 바탕 으로 쓴 것 이다)
이상 은 개인 적 인 경험 이 므 로 여러분 에 게 참고 가 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.만약 잘못 이 있 거나 완전히 고려 하지 않 은 부분 이 있다 면 아낌없이 가르침 을 주시 기 바 랍 니 다.

좋은 웹페이지 즐겨찾기