Spring Boot 1.3.x の Web アプリを 1.4.x へバージョンアップする ( その13 )( RestTemplate で WebAPI を呼び出している処理に spring-retry でリトライ処理を入れる )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
- 前回の検証で WebAPI を呼び出している処理にリトライ処理を入れればテストが成功することが分かったので、spring-retry でリトライ処理を入れます。
- @Retryable アノテーションではなく RetryTemplate クラスで実装します。
- リトライ処理は、以下のルールにします。
- Exception.class ( あるいはその継承クラス ) が throw されたらリトライします。
- 最大5回リトライします。
- リトライ間隔は5秒固定にします。
- リトライが全てエラーになった場合には、発生した例外がそのまま throw されるようにします。
- spring-retry の処理については以前書いた以下の記事を参考にして実装します。
参照したサイト・書籍
目次
手順
build.gradle を変更する
gradlew dependencies
を実行すると org.springframework.boot:spring-boot-starter-amqp
の依存関係で spring-retry は入っていることが確認できたのですが、
compile - Dependencies for source set 'main'. .......... +--- org.springframework.boot:spring-boot-starter-amqp: -> 1.4.4.RELEASE | +--- org.springframework.boot:spring-boot-starter:1.4.4.RELEASE (*) | +--- org.springframework:spring-messaging:4.3.6.RELEASE | | +--- org.springframework:spring-beans:4.3.6.RELEASE (*) | | +--- org.springframework:spring-context:4.3.6.RELEASE (*) | | \--- org.springframework:spring-core:4.3.6.RELEASE | \--- org.springframework.amqp:spring-rabbit:1.6.7.RELEASE | +--- org.springframework:spring-web:4.2.9.RELEASE -> 4.3.6.RELEASE (*) | +--- org.springframework.retry:spring-retry:1.1.5.RELEASE | | \--- org.springframework:spring-core:4.0.4.RELEASE -> 4.3.6.RELEASE | +--- org.springframework:spring-messaging:4.2.9.RELEASE -> 4.3.6.RELEASE (*) | +--- org.springframework:spring-context:4.2.9.RELEASE -> 4.3.6.RELEASE (*) | +--- org.springframework:spring-tx:4.2.9.RELEASE -> 4.3.6.RELEASE (*) | +--- com.rabbitmq:http-client:1.0.0.RELEASE | | +--- org.apache.httpcomponents:httpclient:4.3.6 -> 4.5.2 | | | +--- org.apache.httpcomponents:httpcore:4.4.4 -> 4.4.6 | | | \--- commons-codec:commons-codec:1.9 -> 1.10 | | \--- com.fasterxml.jackson.core:jackson-databind:2.5.1 -> 2.8.6 (*) | +--- org.springframework.amqp:spring-amqp:1.6.7.RELEASE | | \--- org.springframework:spring-core:4.2.9.RELEASE -> 4.3.6.RELEASE | \--- com.rabbitmq:amqp-client:3.6.5 -> 3.6.6 ..........
明示的に build.gradle に記述することにします。
- build.gradle を リンク先の内容 に変更します。変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
spring-retry でリトライ処理を入れる
src/main/java/ksbysample/webapp/lending/config/ApplicationConfig.java を リンク先の内容 に変更します。
src/main/java/ksbysample/webapp/lending/service/calilapi/CalilApiService.java を リンク先の内容 に変更します。
動作確認
clean タスク → Rebuild Project → build タスク を実行して “BUILD SUCCESSFUL” が表示されることを確認します。
Project Tool Window の src/test から「Run ‘All Tests’ with Coverage」を実行して、テストが全て成功することを確認します。
メモ書き
メソッドに @Retryable アノテーションを付けるだけでリトライしてくれる訳ではないらしい。@Retryable アノテーションでリトライさせるための条件を忘れているな。。。
ソースコード
build.gradle
dependencies { .......... // dependency-management-plugin によりバージョン番号が自動で設定されるもの // Appendix A. Dependency versions ( http://docs.spring.io/platform/docs/current/reference/htmlsingle/#appendix-dependency-versions ) 参照 .......... compile("org.springframework.session:spring-session") compile("org.springframework.retry:spring-retry") compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") ..........
compile("org.springframework.retry:spring-retry")
を追加します。
ApplicationConfig.java
@Configuration public class ApplicationConfig { .......... @Bean public RetryTemplate simpleRetryTemplate() { RetryTemplate retryTemplate = new RetryTemplate(); retryTemplate.setRetryPolicy( new SimpleRetryPolicy(5, singletonMap(Exception.class, true))); FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(5000); retryTemplate.setBackOffPolicy(fixedBackOffPolicy); return retryTemplate; } }
- simpleRetryTemplate メソッドを追加します。
CalilApiService.java
@Service @PropertySource("classpath:calilapi.properties") public class CalilApiService { .......... private final RestTemplate restTemplateForCalilApi; private final RestTemplate restTemplateForCalilApiByXml; private final RetryTemplate simpleRetryTemplate; public CalilApiService(@Qualifier("restTemplateForCalilApi") RestTemplate restTemplateForCalilApi , @Qualifier("restTemplateForCalilApiByXml") RestTemplate restTemplateForCalilApiByXml , @Qualifier("simpleRetryTemplate") RetryTemplate simpleRetryTemplate) { this.restTemplateForCalilApi = restTemplateForCalilApi; this.restTemplateForCalilApiByXml = restTemplateForCalilApiByXml; this.simpleRetryTemplate = simpleRetryTemplate; } .......... public Libraries getLibraryList(String pref) throws Exception { // 図書館データベースAPIを呼び出して XMLレスポンスを受信する ResponseEntity<String> response = getForEntityWithRetry(this.restTemplateForCalilApi , URL_CALILAPI_LIBRALY, String.class, this.calilApiKey, pref); // 受信した XMLレスポンスを Javaオブジェクトに変換する Serializer serializer = new Persister(); Libraries libraries = serializer.read(Libraries.class, response.getBody()); return libraries; } public LibrariesForJackson2Xml getLibraryListByJackson2Xml(String pref) throws Exception { // 図書館データベースAPIを呼び出して XMLレスポンスを受信する ResponseEntity<LibrariesForJackson2Xml> response = getForEntityWithRetry(this.restTemplateForCalilApiByXml , URL_CALILAPI_LIBRALY, LibrariesForJackson2Xml.class, this.calilApiKey, pref); return response.getBody(); } public List<Book> check(String systemid, List<String> isbnList) { .......... ResponseEntity<CheckApiResponse> response = null; String url = URL_CALILAPI_CHECK; for (int retry = 0; retry < RETRY_MAX_CNT; retry++) { // 蔵書検索APIを呼び出して蔵書の有無と貸出状況を取得する response = getForEntityWithRetry(this.restTemplateForCalilApiByXml, url, CheckApiResponse.class, vars); logger.info("カーリルの蔵書検索API を呼び出し、レスポンスを取得しました。{}", response.getBody().toString()); if (response.getBody().getContinueValue() == 0) { break; } .......... } return response.getBody().getBookList(); } private <T> ResponseEntity<T> getForEntityWithRetry(RestTemplate restTemplate, String url , Class<T> responseType, Object... uriVariables) { ResponseEntity<T> response = this.simpleRetryTemplate.execute(context -> { if (context.getRetryCount() > 0) { logger.info("★★★ リトライ回数 = " + context.getRetryCount()); } ResponseEntity<T> innerResponse = restTemplate.getForEntity(url, responseType, uriVariables); return innerResponse; }); return response; }
private final RetryTemplate simpleRetryTemplate;
を追加し、コンストラクタインジェクションの処理も追加します。- getForEntityWithRetry メソッドを追加します。
- 以下のメソッド内で RestTemplate#getForEntity を呼び出しているところを getForEntityWithRetry メソッドを使用するように変更します。
- getLibraryList メソッド
- getLibraryListByJackson2Xml メソッド
- check メソッド
履歴
2017/03/20
初版発行。
2017/03/22
* @Retryable アノテーションで実装したのですが、例外が throw されてもリトライしないことに気付いたので、RetryTemplate クラスを使用する方法に修正しました。