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();
      }复制代码

    }

三つの方法:方法と、ほぼ2、1、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;
    }复制代码

4つの方法: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ベース)

  • 力に非同期呼び出し@Asyncクラスのノートを開始@EnableAsyncを追加する必要があります
  • この方法で参加すること@Async(「スレッドプール」)、スレッド・プール・カスタム・スレッド・プールにこのコメントの非同期実行を要求します
  • 少しのコード。2つのラベルでは、人は自分自身をテストすることができるようになります

    3ノート

  • デフォルトでのTaskExecutorが設定されていないとき、デフォルトではSimpleAsyncTaskExecutorに、このスレッドプールを使用することですが、スレッドは再利用、各呼び出しは新しいスレッドが作成されますされませんので、スレッドプールは、このスレッドの本当の意味ではありません。コンソールログ出力を通して見ることができ、各スレッド名の出力が増加されます。最高の私たちは、プールからスレッドを定義します。
  • 非同期メソッド呼び出し、あなたが起動時にスキャン春は同じ呼び出しながら、プロキシクラスを作成し、代理自体を呼び出すための方法は、簡単な言葉で、(同じクラスの内部クラスを含む)を同じクラスにすることはできませんクラスなので、通常の呼び出しは同じです。このよう@Cacheなどのような他のコメントははっきり、原因とSpringのプロキシメカニズムであり、同じ理由です。だから、開発では、管理するためのクラスのうち、非同期サービスを分離するのが最善です。ハイライトする機会の下に。

    図4は、どのような状況下で失敗@Async非同期メソッドにつながるのでしょうか?

  1. 同じクラスを呼び出すと@Async非同期メソッドの賭けを持っています
    :@Asyncとして春とトランザクション@、エージェントの動的な性質を利用してキャッシュし、他の注釈は、実際には、春のコンテナクラスのオブジェクトは、SpringコンテナがAOPのアノテーションは、プロキシオブジェクト(理解するために単純なので)「を置き換える」が含まれますときに初期化何Springコンテナがないため、呼び出し元のメソッドは、オブジェクト自体ではなく、プロキシオブジェクトであるため、失敗の理由は、非常に明確であることをノートには、その解決策を解決するために、この考え方に沿ったものになります。
  2. スタティック(静的)と呼ばれる方法
  3. 民営化のコール(プライベート)メソッド

    1の問題4を解決する5つの方法(2,3-その上に彼らの注意の下に2つの他の問題)

  4. 別デシメーションクラスとして非同期で実行する方法
    、原則はあなたが単一のクラスに抽出する非同期メソッドを実行すると、このクラスは、あなたが実際にそれがプロキシクラスに注入されたときに、他の春のコンポーネントは、確かに、それに注入されます呼び出す必要が春、によって管理されていなければならないということです。
  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 =真)
      ノート。
    • コードは次のように実装されています。

      @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の機関である、また、〜のサポートハの多くを願って、動的プロキシの詳細な解釈に従います

その他の技術資料は、マイクロチャネルの公共の番号に注意を払うを喜ば:Javaプログラマの集い



おすすめ

転載: juejin.im/post/5e05f9b3518825124653cd8f