はじめに
CAPからリモートサービスを呼び出すとき、マスタデータなど変更が少ないデータはCAP側でキャッシュしておくことで、レスポンスタイムを短縮することができます。
また、リモートサービスが一時的に落ちていた場合は、何度かリトライすることで接続できる可能性があります。
CAP自体にはキャッシュのやリトライ仕組みはありませんが、SAP Cloud SDKのResilience機能を使うことでこれらの機能を組み込むことができます。
シナリオ
CAP(Java)からリモートのBusiness Partnerサービスにアクセスし、データを取得します。
「Business Partnerサービス」は、ログなどを見られるようにローカルで作成します。
以下が作成したプロジェクトです。
バージョン
- SAP Cloud SDK: 5.22.0
- @sap/cds-dk: 9.3.0
- CAP Java SDK: 4.2.0
前提
Business Partnerサービスのメタデータをプロジェクトに取り込み、以下のエンティティを定義します。(以下のブログのステップ1~2を参照)
https://qiita.com/tami/items/441aae3330cb1e678ef2
using { BusinessPartnerService as external } from './external/BusinessPartnerService.cds';
service ConsumeBPService {
entity LocalBusinessPartners as projection on external.BusinessPartners;
}
キャッシュとリトライの実装
以下のステップでキャッシュとリトライ機能を実装します。
- Resilience APIをDependencyに追加
- キャッシュの実装
- リトライの実装
1. Resilience APIをDependencyに追加
プロジェクトルートのpom.xml
に以下の設定を追加します。
<properties>
<cloud.sdk.version><最新のSDKバージョン></cloud.sdk.version>
...
</properties>
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>com.sap.cloud.sdk</groupId>
<artifactId>sdk-modules-bom</artifactId>
<version>${cloud.sdk.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
次に、srv/pom.xml
に以下の設定を追加します。
<dependencies>
<!-- SAP CLOUD SDK RESILIENCE MODULE -->
<dependency>
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
<artifactId>resilience</artifactId>
</dependency>
<!-- JCache -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>jcache</artifactId>
<version>3.1.8</version>
<scope>runtime</scope>
</dependency>
</dependencies>
2. キャッシュの実装
2.1. キャッシュ実装前
キャッシュを実装する前のイベントハンドラは以下のようになっています。
@Component
@ServiceName(ConsumeBPService_.CDS_NAME)
public class ConsumeBPServiceHandler implements EventHandler {
@Autowired
@Qualifier(BusinessPartnerService_.CDS_NAME)
CqnService bupa;
@On(event = CqnService.EVENT_READ, entity = LocalBusinessPartners_.CDS_NAME)
public Result onReadBusinessPartners(CdsReadEventContext context) {
// リモートサービスでクエリを実行
return bupa.run(context.getCqn());
}
}
2.2. キャッシュ実装
以下のステップで、リモートサービスの呼び出し結果をキャッシュします。
-
CacheConfigurationを作成:
CacheConfiguration
にはキャッシュの有効期間とパラメータ(キャッシュを保管する際のキー)を指定する。このほかに、有効期間の計算の起点を表すexpirationStrategyを指定することもできる -
ResilienceConfigurationを作成:
ResilienceConfiguration
を作成し、キャッシュの設定を渡す -
リモートサービスを呼び出し:
ResilienceDecorator.executeSupplier()
でリモートサービスの呼び出しを実行する。引数にリモートサービスの呼び出し処理と、ResilienceConfiguration
を渡す
参考:Class ResilienceConfiguration.CacheConfiguration
@On(event = CqnService.EVENT_READ, entity = LocalBusinessPartners_.CDS_NAME)
public Result onReadBusinessPartners(CdsReadEventContext context) {
String queryKey = context.getCqn().toString();
// CacheConfigurationを作成
ResilienceConfiguration.CacheConfiguration cacheConfig = ResilienceConfiguration.CacheConfiguration
.of(Duration.ofSeconds(30)) // キャッシュの有効期間
.withParameters(queryKey); //キャッシュのパラメータを指定
// ResilienceConfigurationを作成
ResilienceConfiguration resilienceConfig = ResilienceConfiguration.of(BusinessPartnerService.class)
.cacheConfiguration(cacheConfig);
// リモートサービス呼び出し
Result result = ResilienceDecorator.executeSupplier(() -> callBusinessPartnerService(context), resilienceConfig);
return result;
}
private Result callBusinessPartnerService(CdsReadEventContext context) {
return bupa.run(context.getCqn());
}
キャッシュのパラメータについて
パラメータを指定しない場合、クエリ条件を変えても同じ結果(キャッシュされた結果)が返ってきました。パラメータを指定することで実行条件と結果をひもづけておく必要があります。
2.3. 動作確認
リモートサービスへのアクセスのアクセス時間を計測できるようにログを追加して、動作を確認してみます。
Instant startTime = Instant.now();
// リモートサービスへのアクセス
long totalTimeMs = Duration.between(startTime, Instant.now()).toMillis();
logger.info("Request completed - ExecutionTime: {}ms", totalTimeMs);
以下のリクエストでビジネスパートナー全件(10件)を取得します。
GET {{server}}/odata/v4/ConsumeBPService/LocalBusinessPartners
Authorization: Basic {{username}}:{{password}}
キャッシュを使うことで、2回目以降のレスポンスが早くなっています。
キャッシュされたデータはメモリに格納されるので、大量のデータを少しずつ条件を変えながら取得するケースではメモリの使用量が増加すると予想されます。キャッシュ対象のリクエストとキャッシュの保存期間について、慎重に検討する必要があります。
3. リトライの実装
3.1. リトライ実装
リトライ機能もResilienceConfiguration
の一部として提供されています。以下では、ステップ2.で作成したResilienceConfigurationにリトライの設定を追加します。
-
RetryConfigurationを作成:
RetryConfiguration
にはリトライの回数と、リトライする前の待ち時間などを指定できる
参考:Class ResilienceConfiguration.RetryConfiguration
// RetryConfigurationを作成
ResilienceConfiguration.RetryConfiguration retryConfig = ResilienceConfiguration.RetryConfiguration
.of(3)
.waitDuration(Duration.ofSeconds(10));
// キャッシュの設定(省略)
// Create Resilience Configuration with Cache and Retry settings
ResilienceConfiguration resilienceConfig = ResilienceConfiguration.of(BusinessPartnerService.class)
.retryConfiguration(retryConfig)
.cacheConfiguration(cacheConfig);
3.2. 動作確認
リモートサービスを停止させた状態で実行します。3回呼び出しを行った後、エラーで終了しました。
1回目の呼び出しの直後にリモートサービスを起動すると、2回目のリクエストは成功し、正常にレスポンスが返ってきます。
おわりに
この記事では、SAP Cloud SDKを使いCAP Javaのアプリケーションにキャッシュとリトライ機能を組み込みました。複雑なコーディングなしにこれらの機能を使えるのがうれしく感じました。