多くの非同期呼び出しとハンドラーを持つAPIを使用しています。これらをRESTfulインターフェースと春のエンドポイントで拡張したいと思います。通常のコントローラー層とサービス層を想像していますが、サービスはおそらくこれらの非同期ハンドラーをラップしています。私はCompletableFuture、@ Async、および@EnableAsyncに関するいくつかの記事を読み、最終的にはこれを機能させましたが、「正しく」または適切に設計されているかどうかはわかりません。 この投稿 は、CompletableFutureハンドラーと非同期ハンドラーをマージするのに優れています。少なくとも、ハンドラーは私のものと似ています。
だから私はこのインターフェースのようなものを持っています:
interface AccountHandler {
void onSuccess(Portfolio result);
void onError(Throwable error);
}
そして今、このCompletableFutureはそれとマージしました:
class MyPortfolioCallbackHandler extends CompletableFuture<Portfolio> implements AccountHandler {
void onSuccess(Portfolio result) {
super.complete(result);
}
void onError(Throwable error) {
super.completeExceptionally(error);
}
}
サービスは次のようになります。
@Service
public class PortfolioService {
@Async
public CompletableFuture<Portfolio> getPortfolio() throws Exception {
MyPortfolioCallbackHandler portfolioHandler = new PortfolioHandler();
portfolioHandler.getPortfolio();
return portfolioHandler;
}
}
そしてコントローラーはこれです:
@RestController
@RequestMapping("portfolio")
public class PortfolioController {
@Autowired
PortfolioService portfolioService;
@GetMapping(produces = "application/json")
public Portfolio getPortfolio() throws Exception {
return portfolioService.getPortfolio();
}
}
驚いたことに、これは実際に機能しますが、いくつかのことを試して、CompletableFutureに混乱しました。しかし、それは良いデザインですか、それとも「正しく」行われますか?
Javaによる_CompletableFuture<T>
_の実装については無知であることは認めますが、一般的には、非表示の同期呼び出しによるデッドロックの脅威が発生する可能性があります。この特定の問題は、C#async
およびawait
タスクの同期で明らかに明らかです。
portfolioHandler.get()
メソッド内に隠されているのは、ロックが解放されるのを待っている同期呼び出しです。これを、Webサーバーのスレッドプールで実際に実行されるスレッドの予測できない性質と組み合わせると、CompletableFuture
を処理するはずのスレッドを常にブロックするリスクがあります。
少なくとも、私はSpring MVCでこのアプローチを試みません
私は本質的に非同期であることになっているSpring Fluxの使用を検討します。 C#は、非同期ハンドラーを導入することにより、非同期スレッドロックのリスクを処理しました。非同期Webインフラストラクチャと非同期バックエンドコールを一致させることで、インピーダンスの不一致を回避できます。