Springboot@Asyncを利用してAPIインタフェースの同時能力を向上


概要

  • あなたはまだいくつかのインタフェースの業務が複雑で、応答速度が遅く、同時量が行かないことに悩んでいるかどうか、今日は複雑な業務をどのように分割し、デカップリングするかをお勧めします.高同時に直面してこの5点を覚えることができます:1、非同期、ピークを削って谷を埋めます;2、キャッシュ、比較的安定した高周波ホットスポットデータをキャッシュし、業務ロジックを実行する性能オーバーヘッドを低減する.3、並行して、業務応答時間を短縮する;4、あなたの業務コードを最適化し、効率的に業務ロジックを実行する.5、ストリーム制限とダウングレードにより、コアサービスが高く、正常に動作するように保護されます.

  • ここでは、APIインタフェースの応答時間を短縮し、システム単位時間の同時量とスループットを向上させる方法について説明します.
  • 適用シーン解析
  • 一般的なソリューションとテクニック
  • インタフェース非同期化ツール@Asyncの紹介
  • 非同期化アプリケーション起動クラス構成
  • @Asyncを利用して業務並列を実現し、インタフェース応答速度
  • を向上させる.
    転送ゲート
    注意:springboot 2に基づいています.1.3.RELEASEバージョン

    1、シーン分析と最適化方案を適用する

  • 集約クエリーインタフェースは、すべてのバックエンドapiを返す応答データを組み立てる必要があります.
    このようなインタフェースは従来のプログラミング思考に従って、すべてのバックエンドapiをシリアルに呼び出してから組み立てることで、インタフェース応答時間が集約されたインタフェース数に比例して増加し、時間複雑度O(N)をもたらす.
    最適化シナリオ
  • パラレル非同期呼び出しバックエンドapiインタフェース
  • メインスレッドは、すべてのインタフェース呼び出しをリスニングし、最後の実行が完了するまで
  • に戻る.
    解析:このように最適化すると,我々の集約クエリクラスインタフェースの応答時間は,最も遅いバックエンドapiインタフェース速度に相当するだけで,時間複雑度はO(1)に低下する.
    注:この方法では、集約クエリーインタフェース内の各ビジネスインタフェース間で依存関係がないことが要求されます.
  • 複雑なマルチIO型トラフィックインタフェースは、トラフィックロジックがIO操作を待つことが多い.
    このようなインタフェースは、大きなトラフィック圧力に直面すると、サービスの同時性とスループットが低下することが多いが、サーバのCPU、メモリなどのリソースが十分である.
    最適化シナリオ
  • インタフェースの応答内容への影響を業務別に分割し、インタフェースの応答データに関する業務ロジックをすべて持ち出し、残りの業務プロセスは必要に応じて各段階で非同期化処理を行い、この部分はMQ、非同期スレッド処理など
  • を行うことができる.
  • は、持ち出した業務ロジックの一部を分割し、並列処理可能な業務を探し出して非同期並列実行を行い、非同期実行のデータに依存する場合、非同期業務の実行が成功した後に処理を行うことができる.

  • 分析:最適化思想は、まずインタフェース応答処理とあまり関係のない業務の一部を切り捨て、バックグラウンドで非同期に処理させることである.さらに他の業務操作を業務操作間の依存関係の有無に応じて分割し、並列にできるだけ非同期で並列に実行することができる.最終的に応答内容を統一的に処理して返す.
  • 複雑なマルチコンピューティング型ビジネスインタフェース、複数の複雑な時間の計算プロセス.
    このようなインタフェースは、多くのCPUを消費し、サーバの同時およびスループットが低下します.
    最適化シナリオ:
  • サーバCPU構成
  • を追加する.
  • データのリアルタイム性の要求が高くなければ、インタフェース応答に対して一定の時効のキャッシュを行うことができる.
  • 計算ロジックをまず業務範囲から分割し、異なるサービスに渡して実行する.
  • サービスインスタンスの数を増やし、分散計算方法MapReduceを採用する.

  • その他のインタフェースのパフォーマンスボトルネックシーン
    DB、キャッシュ、キュー、またはサーバコンポーネントのパフォーマンスのボトルネックがこの記事の内容ではない場合は、後で専門的な説明があります.

  • 2、よく使うソリューションとテクニック

  • 非同期、ピークを削って谷を埋める.
  • とは、一般に、瞬時の大トラフィック要求をメッセージキューに配置し、システムを1つずつ処理させ、瞬時トラフィックのバリがサービスを完全に利用できないことを意味する.
  • は、サーバが他の期間においても負荷を維持し、サーバの性能を節約することができる.
  • はまた、システムが突然の高負荷に直面したときに、新しいサービスインスタンスを追加してサービスを行う時間を残している.

  • はキャッシュし、比較的安定した高周波ホットスポットデータをキャッシュし、バックエンド業務サービスとミドルウェアサービスに対する性能オーバーヘッドを低減する.
  • は一般的にホットスポットデータをキャッシュすることに分けられる.
  • は、高周波アクセスインタフェース応答をキャッシュする.
  • キャッシュはまた分布式キャッシュ、ローカルキャッシュに分けられ、各サーバミドルウェアも各レベルのキャッシュを提供している.

  • 並列で、業務応答時間を短縮する.
  • 並列可能な業務を剥離し、非同期並列実行し、全体の業務実行時間を短縮し、システム単位時間のスループットを向上させる.

  • あなたのビジネスコードを最適化し、ビジネスロジックを効率的に実行します.
  • これは言うまでもなく、実際の状況に基づいて業務実行プロセスを整理し、合理的な最適化を行えばよい.

  • ストリーム制限とダウングレードは、コアサービスが高く、正常に動作することを保護します.
  • これは一般的に流量入口で制限され、私たちのバックエンド業務が大流量に破壊されないことを保証します.
  • 限流には多くの戦略があり、よく使われるのはユーザーのアクセス頻度を制御し、全体の流量を制御し、バックエンドの業務処理が来ないことを避ける.
  • の降格は、実は最後の無頼な行為であり、腕を切って生計を立て、すべての資源を核心業務に提供し、核心業務の正常なサービスを保証し、エッジ業務はサービスを一時停止する.


  • 3、インタフェース非同期化ツール@Asyncの紹介


    概要:
    このツールはあなたのアプリケーションに便利で速い非同期化のために業務を実行する能力を提供することができて、1つの注釈@Asyncを追加するだけであなたの業務を非同期に実行することができて、ここの非同期実行は、新しいスレッドを開いてあなたの業務を実行することを指します;この注記はクラスにもメソッドにも使用できます.
    3.1コンポーネントの紹介
  • @EnableAsync非同期化機能注記を有効にする
    この注記はspringbootのConfigクラスまたは起動クラスに構成され、非同期化能力をオンにし、非同期化スレッドプールや関連コンポーネントの初期化作業を行うことを推奨します.
  • @Async非同期化モード注記を開く@Async寸法に基づく方法を非同期方法と呼ぶ.これらのメソッドは実行時に独立したスレッドで実行され、呼び出し者はその完了を待つ必要がなく、他の操作を継続することができます.
    この注釈はクラスに表示され、クラスを呼び出すすべての方法が自動的に非同期で実行されます.メソッドにマークアップすると、このメソッドは非同期で実行されます.メソッドが呼び出されると、Asyncのフェースは、非同期スレッドプールにスレッドを申請し、そのスレッドを使用してメソッド内のビジネスロジックを実行します.
  • AsyncConfigurerグローバル構成インタフェース
    カスタム非同期スレッドプールと非同期スレッド実行例外キャプチャを構成し、適切なスレッドプールと例外処理ルールを柔軟にカスタマイズできます.
  • AsyncUncaughtExceptionHandler非同期化運転時グローバル異常キャプチャインタフェース
    非同期スレッドプールの実行時の例外統一処理スキームをカスタマイズします.
  • AsyncExecutor非同期化実行スレッドプール
    非同期実行スレッドプールのサイズ、スレッド生存時間、キュー情報などをカスタマイズします.詳細は、スレッドプールの使用説明を参照してください.ここでは説明しません.

  • 3.2非同期化方法の使用例と説明
    説明:
  • 非同期化注釈@Asyncの表記方法の戻り値はvoidまたはFuture
  • のみである.
  • @Asyncで修飾する方法はstaticタイプとして定義しないでください.これにより、非同期呼び出しは
  • で有効になりません.
  • @Async修飾メソッドは@Transactionalと一緒に使用できません.新しいサブスレッドがメソッド内のトラフィックを実行するために有効になるため、メインスレッド内のトランザクション注釈はサブスレッドのトラフィック操作を制御できません.これは、トランザクションがスレッド分離されているためです.トランザクションを追加する場合は、メソッド内に他のトランザクション注釈を埋め込んだメソッドが有効になります.
  • 例:
  • 無パラメータ非同期化インタフェース
    @Async
    public void executeTask(){
        // 
        
    }
    
  • パラメータ付き非同期化インタフェース
    @Async
    public Future<Dto> task2(){
        // 
        
        // 
        return new AsyncResult<>(new Dto("danyuan",22));
    }
    
    @Data
    @AllArgsConstructor
    public class Dto implements Serializable{
    	/** 
    	 *serialVersionUID
    	 */
    	private static final long serialVersionUID = 1L;
    
    	private String name;
    	
    	private Integer age;
    	
    }
    
  • 4、非同期化アプリケーション起動クラス構成


    アプリケーション構成起動クラスの例は次のとおりです.
    /**  
    * Title StartAsyncServer.java  
    * Description  
    * @author danyuan
    * @date Mar 8, 2020
    * @version 1.0.0
    * site: www.danyuanblog.com
    */ 
    package com.danyuanblog.test;
    
    import java.io.ByteArrayOutputStream;
    import java.io.PrintStream;
    import java.lang.reflect.Method;
    import java.util.concurrent.Executor;
    
    import lombok.extern.slf4j.Slf4j;
    
    import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.AsyncConfigurer;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    @SpringBootApplication
    @EnableAsync
    public class StartAsyncServer implements AsyncConfigurer {
    
    	public static void main(String[] args) {
    		SpringApplication.run(StartAsyncServer.class, args);
    	}
    	
    	@Override
    	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    		
    		return new ExceptionHandler();
    	};
    	
    	@Slf4j(topic=" ")
    	static class ExceptionHandler implements AsyncUncaughtExceptionHandler{
    
    		/**
    		 * @author danyuan
    		 */
    		@Override
    		public void handleUncaughtException(Throwable e, Method method,
    				Object... params) {// 
    			ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
    	        PrintStream printStream = new PrintStream(outputStream);
    	        e.printStackTrace(printStream);
    	        log.error(outputStream.toString());
    		}
    		
    	}
    	/**
    	 * @author danyuan
    	 */
    	@Override
    	public Executor getAsyncExecutor() {
    		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(20);
            executor.setMaxPoolSize(400);
            executor.setQueueCapacity(10000);
            executor.setThreadNamePrefix("TestAsyncExecutor-");
            executor.initialize();
            return executor;
    	}
    }
    

    5、@Asyncを利用して業務並列を実現し、インタフェースの応答速度を向上する


    ビジネスシーンは、task 1、task 2の間に依存関係がなく、task 3依存とtask 2の操作結果であり、コード例は以下の通りである.
  • AsyncTestService.java
    /**  
    * Title AsyncTestService.java  
    * Description  
    * @author danyuan
    * @date Mar 13, 2020
    * @version 1.0.0
    * site: www.danyuanblog.com
    */ 
    package com.danyuanblog.test.asyc;
    
    import java.util.concurrent.Future;
    
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.scheduling.annotation.AsyncResult;
    import org.springframework.stereotype.Service;
    
    @Service
    public class AsyncTestService {
    	
    	@Async
    	public void task1(){
    		System.out.println("task1 execute begin .....");
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println("task1 execute success !");
    	}
    	
    	@Async
    	public Future<Dto> task2(){
    		System.out.println("task2 execute begin .....");
    		try {
    			Thread.sleep(6000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println("task2 execute success !");
    		return new AsyncResult<>(new Dto("danyuan",22));
    	}
    	
    	@Async
    	public void task3(Dto dto){
    		System.out.println("task3 execute begin .....");
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		if(dto.getAge() > 18){
    			System.out.println(dto.getName()+" !");
    		}else{
    			System.out.println(dto.getName()+" !");
    		}
    		System.out.println("task3 execute success !");
    	}
    	
    }
    
  • AsycTestController.java
    /**  
    * Title AsycTestController.java  
    * Description   
    * @author danyuan
    * @date Mar 8, 2020
    * @version 1.0.0
    * site: www.danyuanblog.com
    */ 
    package com.danyuanblog.test.asyc;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Future;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.AsyncResult;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class AsycTestController {
    	
    	@Autowired
    	private AsyncTestService asyncTestService;
    	/**
    	 *  
    	 * @author danyuan
    	 */
    	@GetMapping("/testAsync")
    	public void testAsync(){
    		asyncTestService.task1();
    		Future<Dto> result = asyncTestService.task2();
    		while(true){//task3 task2 
    			if(result.isDone() || result.isCancelled()){
    				break;
    			}
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		try {
    			asyncTestService.task3(result.get());
    		} catch (ExecutionException | InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    }