2017-05-11 120 views
0

外部Webサービスコール時にタイムアウトを設定しようとしています。私は私のサービスでSpring Rest Templateによって外部Webサービスを呼び出すことになっています。Spring RestTemplate接続タイムアウトが機能していません

接続タイムアウトをテストする目的で、外部Webサービスが停止され、アプリケーションサーバーが停止しています。

私はタイムアウトに10秒を設定しましたが、残念ながら1秒後に接続拒否の例外が発生します。

try { 
    final RestTemplate restTemplate = new RestTemplate(); 

    ((org.springframework.http.client.SimpleClientHttpRequestFactory) 
     restTemplate.getRequestFactory()).setReadTimeout(1000*10); 

    ((org.springframework.http.client.SimpleClientHttpRequestFactory) 
     restTemplate.getRequestFactory()).setConnectTimeout(1000*10); 

    HttpHeaders headers = new HttpHeaders(); 
headers.setContentType(MediaType.APPLICATION_JSON); 

    HttpEntity<String> entity = new HttpEntity<String>(reqJSON, headers); 

    ResponseEntity<String> response = restTemplate.exchange(wsURI, HttpMethod.POST, entity, String.class); 

    String premiumRespJSONStr = response.getBody(); 
} 

私の理解があればそれを修正してください。

+0

タイムアウトを設定せずに接続できますか? – Vaibs

+0

@ Vaibs、テスト目的のための番号私は外部Webサービスを開始しませんでした。指定されたタイムアウトは機能しません。 – Easy2DownVoteHard2Ans

+0

自分の環境でコードをテストしただけです。そのうまく動作します。あなたのサービスはこれをテストするべきです。これを達成するために、私の提案は接続タイムアウト値を最小にすることです。 10msに変更してください。 – Vaibs

答えて

6

以下はconnectTimeoutの設定に関するものです。

ケース - 未知のホスト

あなたが到達できないホスト(例:http://blablablabla/v1/timeout)を持っているなら、あなたはできるだけ早くUnknownHostExceptionを受け取ることになります。 AbstractPlainSocketImpl :: connect() :: !addr.isUnresolved() :: throw UnknownHostExceptionタイムアウトなし。ホストはInetAddress.getByName(<host_name>)を使用して解決されます。

ケース - できるだけ早くConnection refused: connect - 未知のポート

あなたが到達可能であるが、何の接続は行われないことができますホストを持っている場合は、あなたはConnectExceptionを受けます。これは、DualStackPlainSocketImpl :: socketConnect()から呼び出されるネイティブメソッドDualStackPlainSocketImpl :: static native void waitForConnect(int fd, int timeout) throws IOExceptionで発生するようです。タイムアウトは尊重されません。

プロキシ?プロキシが使用されている場合、状況が変化する可能性があります。到達可能なプロキシを持っていると、タイムアウトが発生する可能性があります。

関連する質問チェックthis answerこれはあなたが遭遇した場合に関連しています。

複数のIPアドレスにマップされた同じドメインを持つDNSラウンドロビンは、クライアントが見つかるまで各IPに接続します。したがって、connectTimeout()は、動作していないリストの各IPに対して独自のペナルティを追加します。詳細についてはthis articleをお読みください。

結論connectTimeoutを取得する場合は、独自のリトライロジックを実装するか、プロキシを使用する必要があります。

テストconnectTimeoutあなたはタイムアウトを取得するため、完成からのソケット接続を防ぐエンドポイントを持つのさまざまな方法のthis answerを参照することができます。ソリューションを選択すると、バンプブーツで統合テストを作成し、実装を検証することができます。これは、readTimeoutをテストするために使用される次のテストに似ています。この場合、ソケット接続を妨げるURLに変更することができます。

テストreadTimeout

readTimeoutが最初、従ってサービスをアップする必要がある接続である必要がテストするために。次に、各要求に対して大きな遅延を伴う応答を返すエンドポイントを提供することができる。

次は、統合テストを作成するために、春ブートで行うことができます

1.テスト

@RunWith(SpringRunner.class) 
@SpringBootTest(
     webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 
     classes = { RestTemplateTimeoutConfig.class, RestTemplateTimeoutApplication.class } 
) 
public class RestTemplateTimeoutTests { 

    @Autowired 
    private RestOperations restTemplate; 

    @LocalServerPort 
    private int port; 

    @Test 
    public void resttemplate_when_path_exists_and_the_request_takes_too_long_throws_exception() { 
     System.out.format("%s - %s\n", Thread.currentThread().getName(), Thread.currentThread().getId()); 

     Throwable throwable = catchThrowable(() -> 
       restTemplate.getForEntity(String.format("http://localhost:%d/v1/timeout", port), String.class)); 

     assertThat(throwable).isInstanceOf(ResourceAccessException.class); 
     assertThat(throwable).hasCauseInstanceOf(SocketTimeoutException.class); 
    } 
} 

2.設定RestTemplate

@Configuration 
public class RestTemplateTimeoutConfig { 

    private final int TIMEOUT = (int) TimeUnit.SECONDS.toMillis(10); 

    @Bean 
    public RestTemplate restTemplate() { 
     return new RestTemplate(getRequestFactory()); 
    } 

    private ClientHttpRequestFactory getRequestFactory() { 
     HttpComponentsClientHttpRequestFactory factory = 
       new HttpComponentsClientHttpRequestFactory(); 

     factory.setReadTimeout(TIMEOUT); 
     factory.setConnectTimeout(TIMEOUT); 
     factory.setConnectionRequestTimeout(TIMEOUT); 
     return factory; 
    } 
} 
を作成します。

3.テストスターが起動したときに実行されるSpring Bootアプリ

@Configuration 
public class RestTemplateTimeoutConfig { 

    private final int TIMEOUT = (int) TimeUnit.SECONDS.toMillis(10); 

    // consider that this is the existing RestTemplate 
    @Bean 
    public RestTemplate restTemplate() { 
     return new RestTemplate(); 
    } 

    // this will change the RestTemplate settings and create another bean 
    @Bean 
    @Primary 
    public RestTemplate newRestTemplate(RestTemplate restTemplate) { 
     SimpleClientHttpRequestFactory factory = 
       (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory(); 

     factory.setReadTimeout(TIMEOUT); 
     factory.setConnectTimeout(TIMEOUT); 

     return restTemplate; 
    } 
} 

を設定し、既存のRestTemplate

RequestConfigを使用して新しいRestTemplateをRestTemplate

設定を構成したTS

@SpringBootApplication 
@Controller 
@RequestMapping("/v1/timeout") 
public class RestTemplateTimeoutApplication { 

    public static void main(String[] args) { 
     SpringApplication.run(RestTemplateTimeoutApplication.class, args); 
    } 

    @GetMapping() 
    public @ResponseStatus(HttpStatus.NO_CONTENT) void getDelayedResponse() throws InterruptedException { 
     System.out.format("Controller thread = %s - %s\n", Thread.currentThread().getName(), Thread.currentThread().getId()); 
     Thread.sleep(20000); 
    } 
} 

代替方法

MockRestServiceServerRestTemplateと偽装して、なぜリクエストファクトリを置き換えないのですか。したがって、RestTemplateの設定はすべて置き換えられます。したがって、タイムアウトテストのために実際のアプリケーションを使用することは、ここでは唯一のオプションかもしれません。

注:も確認してくださいthis articleRestTemplateコンフィグレーションにはタイムアウトコンフィグレーションも含まれています。

関連する問題