Springboot@Asyncを利用してAPIインタフェースの同時能力を向上
33568 ワード
概要
ここでは、APIインタフェースの応答時間を短縮し、システム単位時間の同時量とスループットを向上させる方法について説明します.
転送ゲート
注意:springboot 2に基づいています.1.3.RELEASEバージョン
1、シーン分析と最適化方案を適用する
このようなインタフェースは従来のプログラミング思考に従って、すべてのバックエンドapiをシリアルに呼び出してから組み立てることで、インタフェース応答時間が集約されたインタフェース数に比例して増加し、時間複雑度O(N)をもたらす.
最適化シナリオ
解析:このように最適化すると,我々の集約クエリクラスインタフェースの応答時間は,最も遅いバックエンドapiインタフェース速度に相当するだけで,時間複雑度はO(1)に低下する.
注:この方法では、集約クエリーインタフェース内の各ビジネスインタフェース間で依存関係がないことが要求されます.
このようなインタフェースは、大きなトラフィック圧力に直面すると、サービスの同時性とスループットが低下することが多いが、サーバのCPU、メモリなどのリソースが十分である.
最適化シナリオ
分析:最適化思想は、まずインタフェース応答処理とあまり関係のない業務の一部を切り捨て、バックグラウンドで非同期に処理させることである.さらに他の業務操作を業務操作間の依存関係の有無に応じて分割し、並列にできるだけ非同期で並列に実行することができる.最終的に応答内容を統一的に処理して返す.
このようなインタフェースは、多くのCPUを消費し、サーバの同時およびスループットが低下します.
最適化シナリオ:
DB、キャッシュ、キュー、またはサーバコンポーネントのパフォーマンスのボトルネックがこの記事の内容ではない場合は、後で専門的な説明があります.
2、よく使うソリューションとテクニック
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();
}
}
}