SpringBoot 비동기 요청 및 비동기 호출에

원본 링크 : www.cnblogs.com/baixianlong ...

사용 A, SpringBoot 비동기 요청

1 동기 비동기 요청을 요청

동기 요청
비동기 요청

특징 :

  • 스레드가 먼저 요청하는 선박에 할당 된 자원을 해제 할 수있는 경우, 시스템의 부담을 줄이고, 스레드 분배 컨테이너 요청의 릴리스는 응답이 지연되고, 시간이 많이 소요되는 프로세스는 고객에게 다음 완료 (예를 들어, 긴 작동)되고 될 수 있습니다 최종 응답.

    결론 : 클라이언트의 요청에 서버의 처리량을 증가
    (동시 요청의 많은 양의 경우, 우리는 물론, 요청 버퍼 큐 메시지를 통해 수행 할 수 있습니다 공헌의 클러스터 서비스 압력의 노드에로드 요청의 nginx를 통과 할 경우 우리는 실제 생산은 상대적으로 작은 사용) .

    2 비동기 요청

    모드입니다 : 서블릿 방식으로 비동기 요청

      @RequestMapping(value = "/email/servletReq", method = GET)
      public void servletReq (HttpServletRequest request, HttpServletResponse response) {
          AsyncContext asyncContext = request.startAsync();
          //设置监听器:可设置其开始、完成、异常、超时等事件的回调处理
          asyncContext.addListener(new AsyncListener() {
              @Override
              public void onTimeout(AsyncEvent event) throws IOException {
                  System.out.println("超时了...");
                  //做一些超时后的相关操作...
              }
              @Override
              public void onStartAsync(AsyncEvent event) throws IOException {
                  System.out.println("线程开始");
              }
              @Override
              public void onError(AsyncEvent event) throws IOException {
                  System.out.println("发生错误:"+event.getThrowable());
              }
              @Override
              public void onComplete(AsyncEvent event) throws IOException {
                  System.out.println("执行完成");
                  //这里可以做一些清理资源的操作...
              }
          });
          //设置超时时间
          asyncContext.setTimeout(20000);
          asyncContext.start(new Runnable() {
              @Override
              public void run() {
                  try {
                      Thread.sleep(10000);
                      System.out.println("内部线程:" + Thread.currentThread().getName());
                      asyncContext.getResponse().setCharacterEncoding("utf-8");
                      asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
                      asyncContext.getResponse().getWriter().println("这是异步的请求返回");
                  } catch (Exception e) {
                      System.out.println("异常:"+e);
                  }
                  //异步请求完成通知
                  //此时整个请求才完成
                  asyncContext.complete();
              }
          });
          //此时之类 request的线程连接已经释放了
          System.out.println("主线程:" + Thread.currentThread().getName());
      }复制代码

    두 번째 방법 : 매우 간단한 사용은 직접 반환 매개 변수는 호출 층에 싸여 할 수있는 기본 스레드 풀 및 타임 아웃 처리를 설정하는 WebMvcConfigurerAdapter 클래스를 상속 할 수있다

      @RequestMapping(value = "/email/callableReq", method = GET)
      @ResponseBody
      public Callable<String> callableReq () {
          System.out.println("外部线程:" + Thread.currentThread().getName());
    
          return new Callable<String>() {
    
              @Override
              public String call() throws Exception {
                  Thread.sleep(10000);
                  System.out.println("内部线程:" + Thread.currentThread().getName());
                  return "callable!";
              }
          };
      }
    
      @Configuration
      public class RequestAsyncPoolConfig extends WebMvcConfigurerAdapter {
    
      @Resource
      private ThreadPoolTaskExecutor myThreadPoolTaskExecutor;
    
      @Override
      public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
          //处理 callable超时
          configurer.setDefaultTimeout(60*1000);
          configurer.setTaskExecutor(myThreadPoolTaskExecutor);
          configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor());
      }
    
      @Bean
      public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() {
          return new TimeoutCallableProcessingInterceptor();
      }复制代码

    }

세 가지 방법 : 방법과 거의 둘, 하나, WebAsyncTask에 시간 초과 콜백 호출 가능 아웃소싱, 당신이 달성 할 수있는 타임 아웃 처리를 설정

    @RequestMapping(value = "/email/webAsyncReq", method = GET)
    @ResponseBody
    public WebAsyncTask<String> webAsyncReq () {
        System.out.println("外部线程:" + Thread.currentThread().getName());
        Callable<String> result = () -> {
            System.out.println("内部线程开始:" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (Exception e) {
                // TODO: handle exception
            }
            logger.info("副线程返回");
            System.out.println("内部线程返回:" + Thread.currentThread().getName());
            return "success";
        };
        WebAsyncTask<String> wat = new WebAsyncTask<String>(3000L, result);
        wat.onTimeout(new Callable<String>() {

            @Override
            public String call() throws Exception {
                // TODO Auto-generated method stub
                return "超时";
            }
        });
        return wat;
    }复制代码

네 가지 방법 : DeferredResult 몇 가지 상대적으로 복잡한 비즈니스 로직을 처리 할 수있는, 또는 메인 다른 스레드에서 처리 서비스, 2 개 개의 전혀 관계가없는 스레드 사이의 통신에있는 반환 할 수있다.

@RequestMapping(value = "/email/deferredResultReq", method = GET)
    @ResponseBody
    public DeferredResult<String> deferredResultReq () {
        System.out.println("外部线程:" + Thread.currentThread().getName());
        //设置超时时间
        DeferredResult<String> result = new DeferredResult<String>(60*1000L);
        //处理超时事件 采用委托机制
        result.onTimeout(new Runnable() {

            @Override
            public void run() {
                System.out.println("DeferredResult超时");
                result.setResult("超时了!");
            }
        });
        result.onCompletion(new Runnable() {

            @Override
            public void run() {
                //完成后
                System.out.println("调用完成");
            }
        });
        myThreadPoolTaskExecutor.execute(new Runnable() {

            @Override
            public void run() {
                //处理业务逻辑
                System.out.println("内部线程:" + Thread.currentThread().getName());
                //返回结果
                result.setResult("DeferredResult!!");
            }
        });
       return result;
    }复制代码

비동기 호출을 사용하여 두, SpringBoot

1. 소개

비동기 요청 처리. 비동기 요청에 더하여, 우리는 일반적으로 비동기 호출해야합니다 이상을 사용하십시오. 일반적으로 개발 과정에서, 우리는 방법은 실용적이고 비즈니스 관련, 더 견고하지가 발생합니다. 예를 들어, 정보 및 기타 서비스를 로그인합니다. 이번에는 일반 시작에게 약간의 비즈니스 프로세스를 할 수있는 새 스레드를 그래서 다른 기업의 메인 스레드 비동기 실행이.

2, 사용 (Spring 기반)

  • @EnableAsync 강제로 비동기 호출 @Async 클래스 노트를 시작 추가해야
  • 방법에 참여하는 것은 @Async ( "스레드"), 스레드 사용자 지정 스레드 풀에이 댓글의 비동기 실행을 필요로
  • 약간의 코드. . . 두 개의 레이블에, 하나는 자신을 테스트 할 수 있습니다

    3 주

  • 기본적으로 TaskExecutor를 설정하지 않을 경우, 디폴트는 SimpleAsyncTaskExecutor이 스레드 풀을 사용하는 것입니다,하지만 스레드가 재사용, 각 호출은 새 스레드를 생성합니다하지 않기 때문에 스레드 풀은,이 스레드의 진정한 의미하지 않습니다. 콘솔 로그 출력을 통해 볼 수있는 각 스레드 이름의 출력이 증가합니다. 최고의 그래서 우리는 풀에서 스레드를 정의합니다.
  • 비동기 메서드 호출,이 방법을 사용하면 스캔 봄이 프록시 클래스를 생성 할 시작할 때 때문에, 간단한 용어, (같은 클래스의 내부 클래스 포함) 같은 클래스 될 동안 같은 통화 또는 자체 기관을 호출 할 수 없습니다 클래스, 그래서 보통의 호출은 동일합니다. 같은 @Cache 등 다른 의견은, 분명히, 같은 이유 의한 스프링의 프록시 메커니즘입니다. 그래서 개발, 그것을 관리하는 클래스에서 비동기 서비스를 분리하는 것이 가장 좋습니다. 강조 할 수있는 기회 아래. .

    @Async 비동기 방법이 실패에 4, 무엇에 따라 상황이 이어질 것?

  1. 같은 클래스를 호출하면 @Async 비동기 방식의 베팅이 있습니다
    : Spring 컨테이너가 (단순 이해하기 때문에) AOP 주석이 프록시 객체를 "대체"가 포함됩니다 때 @Async 및 트랜잭션 @, 캐시와 에이전트의 동적 특성을 사용하여 다른 주석과 봄, 사실, Spring 컨테이너 클래스 객체는 초기화 더 Spring 컨테이너가 없기 때문에 호출 방법은 개체 자체가 아니라 프록시 객체이기 때문에 실패의 이유는 매우 분명 있다는 노트는 다음 솔루션을 해결하기 위해 생각이 라인을 따라 될 것입니다.
  2. 호출 정적 정적 메소드
  3. 민영화의 통화 (개인) 방법

    (거기에 자신의 관심에 따라 2, 3 개 개의 다른 문제) 1 문제 4를 해결하는 5 가지 방법

  4. 상기 방법은 별도의 데시 메이션 클래스로 비동기 적으로 실행
    , 원리는 하나의 클래스로 추출 비동기 방법을 수행 할 때, 클래스는 당신이 실제로는 프록시 클래스에 주입 할 때, 윌 확실히 주사한다 그것으로 다른 봄의 구성 요소를 호출 할 때, Spring에 의해 관리되어 있어야한다는 것입니다 .
  5. 사실, 우리는 현재 봄 구성 요소에 Spring 컨테이너에서 대상 주입 멤버 변수 할당은, 일부 클래스는 AOP 주석을 사용하고 실제로 실제로 Spring 컨테이너에 존재하는 자사의 프록시 객체입니다. 그럼 우리가 할 수있는

    비동기 메서드를 호출하여 자신의 프록시 개체 컨텍스트를 가져옵니다
    .

    @Controller
    @RequestMapping("/app")
    public class EmailController {
    
        //获取ApplicationContext对象方式有多种,这种最简单,其它的大家自行了解一下
        @Autowired
        private ApplicationContext applicationContext;
    
        @RequestMapping(value = "/email/asyncCall", method = GET)
        @ResponseBody
        public Map<String, Object> asyncCall () {
            Map<String, Object> resMap = new HashMap<String, Object>();
            try{
                //这样调用同类下的异步方法是不起作用的
                //this.testAsyncTask();
                //通过上下文获取自己的代理对象调用异步方法
                EmailController emailController = (EmailController)applicationContext.getBean(EmailController.class);
                emailController.testAsyncTask();
                resMap.put("code",200);
            }catch (Exception e) {
                resMap.put("code",400);
                logger.error("error!",e);
            }
            return resMap;
        }
    
        //注意一定是public,且是非static方法
        @Async
        public void testAsyncTask() throws InterruptedException {
            Thread.sleep(10000);
            System.out.println("异步任务执行完成!");
        }
    
    }复制代码
  6. CGLIB 공개 프록시, 프록시 클래스는 수동으로 봄을 얻을
    비동기 방식에 따라 같은 종류를 호출합니다.
    • 첫째, 시작 클래스를 추가
      @EnableAspectJAutoProxy (exposeProxy = TRUE)
      참고.
    • 다음과 같이 코드가 실행된다 :

      @Service
      @Transactional(value = "transactionManager", readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
      public class EmailService {
      
          @Autowired
          private ApplicationContext applicationContext;
      
          @Async
          public void testSyncTask() throws InterruptedException {
              Thread.sleep(10000);
              System.out.println("异步任务执行完成!");
          }
      
      
          public void asyncCallTwo() throws InterruptedException {
              //this.testSyncTask();
      //        EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);
      //        emailService.testSyncTask();
              boolean isAop = AopUtils.isAopProxy(EmailController.class);//是否是代理对象;
              boolean isCglib = AopUtils.isCglibProxy(EmailController.class);  //是否是CGLIB方式的代理对象;
              boolean isJdk = AopUtils.isJdkDynamicProxy(EmailController.class);  //是否是JDK动态代理方式的代理对象;
              //以下才是重点!!!
              EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);
              EmailService proxy = (EmailService) AopContext.currentProxy();
              System.out.println(emailService == proxy ? true : false);
              proxy.testSyncTask();
              System.out.println("end!!!");
          }
      }复制代码

      셋째, 비동기 호출 비동기 요청의 차이

  • 두 개의 서로 다른 사용 시나리오하여 요청의 처리량을 증가, 서버의 압력에 의한 동시 요청을 해결하기 위해 비동기 요청, 비동기 호출은 비 주요 흐름을 만드는 데 사용됩니다 및 같은 응답 실시간 계산과 작업을 필요로하지 않습니다 메이크업 로그 분석에서 카프카의 동기화 로그.
  • 비동기 요청, 따라서 응답을 기다리는 클라이언트로 결과를 반환 할 필요가있을 것이다, 우리는 즉시 클라이언트 응답에 반환하는 경향이 비동기 호출은, 자신이 천천히 라인에서 실행되는 임무 배경 비동기 호출, 고객으로, 전체 요청을 완료 끝 상관하지 않습니다.

IV 요약

  • 비동기 요청과 같은 주제에 기본적으로 여기에 비동기 호출을 사용하여 여전히 많은 문제가 지적이 기대된다.
  • 이 기사는 ~도 지원 하 많은 희망, 동적 프록시의 상세한 해석을 따를 여기에 원칙의 실현의 동적 프록시, 봄을 동적 AOP 기관입니다 언급

더 많은 기술 문서는 마이크로 채널 대중 번호에주의하시기 바랍니다 : 자바 프로그래머 수집



추천

출처juejin.im/post/5e05f9b3518825124653cd8f