Comments (17)
@parthiv-groww it looks like you need to add options for your feign client to use the same OkHttpClient:
@Bean
Feign feignBuilder(@Qualifier("cdpOkHttpClient") OkHttpClient cdpOkHttpClient) {
return Feign.builder()
.client(new feign.okhttp.OkHttpClient(cdpOkHttpClient))
.options(new Request.Options(cdpOkHttpClient.connectTimeoutMillis(), TimeUnit.MILLISECONDS,
cdpOkHttpClient.readTimeoutMillis(), TimeUnit.MILLISECONDS,
cdpOkHttpClient.followRedirects())
.encoder(new SpringEncoder(messageConverters))
.decoder(new SpringDecoder(messageConverters))
.retryer(new CDPRetryerConfig())
.build();
}
from feign.
Everything works as expected. Retryer was executed by call timeout exception (like 500 server status code). If you don't need to retry, just use Retryer.NEVER_RETRY.
from feign.
Hello @gromspys , thank you for your response.
The problem that I am facing here is using call Timeout in my feign client using Okhttp.
I have tested my both read and connect timeout using the configs that I have shared before. It is working as expected but the real issue is with using call timeout in the similar way.
Okhttp supports call timeout but it is not properly configurable with feign client and hence unable to use it.
Using the (.options) configuration, I am able to use both read and connect timeout but I am not sure how to use that for call timeout.
My retryer is also working as expected.
from feign.
Could you please give your unit test? I tried to reproduce, but it looks like call timeout works as expected:
public interface OkHttpClientTestApi {
@RequestLine("GET /{delay}")
Response get(@Param("delay") String delay);
}
@Test
public void whenCallTimeoutExceeded_thenInterruptedIOException() {
okhttp3.OkHttpClient client = new okhttp3.OkHttpClient.Builder()
.callTimeout(1, TimeUnit.SECONDS)
.build();
okhttp3.Request request = new okhttp3.Request.Builder()
.url("https://httpbin.org/delay/2")
.build();
Throwable thrownClient = catchThrowable(() -> client.newCall(request).execute());
assertThat(thrownClient).isInstanceOf(InterruptedIOException.class);
OkHttpClientTestApi api = Feign.builder()
.client(new OkHttpClient(client))
.target(OkHttpClientTestApi.class, "https://httpbin.org/delay/");
Throwable thrownApi = catchThrowable(() -> api.get("2"));
assertThat(thrownApi).isInstanceOf(RetryableException.class);
}
from feign.
wireMock.stubFor(post(urlPathEqualTo("/v1/readFeatureValues")).willReturn(aResponse().withFixedDelay(1001).withStatus(200)));
Throwable throwable = catchThrowable(() -> cdpDao.getFeatures("user1", CdpRequestDto.builder().build()));
assertThat(throwable).isInstanceOf(InterruptedIOException.class);
This is my unit test . Even I did try with okhttp client in similar manner and it did work but not with feign client.
OkHttpClient client = new OkHttpClient.Builder() .callTimeout(1, TimeUnit.MILLISECONDS) .build(); Request request = new Request.Builder().url("http://localhost:8030").build(); Throwable throwable= catchThrowable(()-> client.newCall(request).execute()); assertThat(throwable).isInstanceOf(InterruptedIOException.class);
And here is how my cdpDao is annotated with Feign client:
@FeignClient(name = "cdp-client", url = "${url}" , configuration = FeignClientConfig.class) public interface CdpDao
One more thing, in the second assert in your code, dont we need to check with "InterruptedIOException.class"
from feign.
As I remember feign client has default retryer. In unit test we can change RetryableException to InterruptedIOException:
assertThat(thrownApi.getCause()).isInstanceOf(InterruptedIOException.class);
from feign.
So can you please suggest any changes that we need to do so that we are able to use call timeout?
from feign.
It's hard to say what is wrong without full configuration. But it looks like your configuration was ignored:
@Bean
Feign feignBuilder(@Qualifier("cdpOkHttpClient") OkHttpClient cdpOkHttpClient) {
return Feign.builder()
.client(new feign.okhttp.OkHttpClient(cdpOkHttpClient))
.encoder(new SpringEncoder(messageConverters))
.decoder(new SpringDecoder(messageConverters))
.retryer(new CDPRetryerConfig())
.build();
}
Try to remove annotation @FeignClient(name = "cdp-client", url = "${url}" , configuration = FeignClientConfig.class)
and build your client manually:
@Bean
CdpDao cdpDao(@Qualifier("cdpOkHttpClient") OkHttpClient cdpOkHttpClient, @Value("url") String url) {
return Feign.builder()
.client(new feign.okhttp.OkHttpClient(cdpOkHttpClient))
.encoder(new SpringEncoder(messageConverters))
.decoder(new SpringDecoder(messageConverters))
.retryer(new CDPRetryerConfig())
.target(CdpDao.class, url);
}
from feign.
Hey @gromspys
I did write an exact test case the way you did .
@Test
public void sampleCallTimeoutTest(){
long start = System.currentTimeMillis();
Throwable throwable = catchThrowable(()-> testDao.get("3"));
long end = System.currentTimeMillis();
log.info("elasped time {}", end-start);
assertThat(throwable).isInstanceOf(InterruptedIOException.class);
}
My sample Interface
public interface TestDao {
@RequestLine("POST /{delay}")
Response get(@Param("delay") String delay);
}
My sample configs
import feign.Feign;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignClientConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public TestDao feignBuilder(@Qualifier("cdpOkHttpClient") okhttp3.OkHttpClient cdpOkHttpClient) {
return Feign
.builder()
.client(new feign.okhttp.OkHttpClient(cdpOkHttpClient))
.target(TestDao.class, "http://localhost:8090");
}
}
package com.example.demo.Config;
import lombok.RequiredArgsConstructor;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
@RequiredArgsConstructor
public class OkHttpClientConfig {
@Bean(name = "baseOkHttpClient")
public okhttp3.OkHttpClient baseOkHttpClient() {
return new okhttp3.OkHttpClient.Builder()
.readTimeout(500,TimeUnit.MILLISECONDS)
.connectTimeout(500, TimeUnit.MILLISECONDS)
.callTimeout(10,TimeUnit.MILLISECONDS)
.build();
}
@Bean(name = "cdpOkHttpClient")
public OkHttpClient cdpOkHttpClient(@Qualifier("baseOkHttpClient") okhttp3.OkHttpClient okHttpClient) {
return okHttpClient.newBuilder()
.readTimeout(500,TimeUnit.MILLISECONDS)
.connectTimeout(500, TimeUnit.MILLISECONDS)
.callTimeout(1000,TimeUnit.MILLISECONDS)
.build();
}
}
My pom.xml file
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>13.2.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>13.1</version>
</dependency>
I set a call timeout of 100ms and started a timer to evaluate the exact time the thread runs for.
Even though the call timeout is set , the thread duration for that particular test is roughly around 1300 ms . With call timeout set, it should probably get terminated.
from feign.
It looks like there is default retryer bacause I'm getting 6300 ms. Try to set .retryer(Retryer.NEVER_RETRY)
from feign.
import feign.Retryer;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignClientConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public TestDao feignBuilder(@Qualifier("cdpOkHttpClient") okhttp3.OkHttpClient cdpOkHttpClient) {
return Feign
.builder()
.client(new feign.okhttp.OkHttpClient(cdpOkHttpClient))
.retryer(Retryer.NEVER_RETRY)
.target(TestDao.class, "http://localhost:8090");
}
}
I have made the suggested changes and my call timeout is 10ms but still my total thread duration is somewhat around 90ms (which suggests that call timeout is not working). And I am not getting the required InterruptedIOException.
from feign.
Need some time for initialization. Try to call twice in the same test and compare time. To get InterruptedIOException exception use throwable.getCause()
from feign.
@Test
public void sampleCallTimeoutTest(){
long start = System.currentTimeMillis();
Throwable throwable = catchThrowable(()-> testDao.get("3"));
long end = System.currentTimeMillis();
long start1 = System.currentTimeMillis();
Throwable throwable1 = catchThrowable(() -> testDao.get("4"));
long end1 = System.currentTimeMillis();
log.info("elasped time {}", end-start);
log.info("elasped second time {}", end1-start1);
assertThat(throwable.getCause()).isInstanceOf(InterruptedIOException.class);
}
This is the changes that I have made, My call timeout is 10ms
2024-03-15T10:41:58.796+05:30 INFO 30665 --- [ main] c.e.demo.Service.PortfolioServiceTest : elasped time 88
2024-03-15T10:41:58.796+05:30 INFO 30665 --- [ main] c.e.demo.Service.PortfolioServiceTest : elasped second time 2
java.lang.AssertionError:
Expecting actual throwable to be an instance of:
java.io.InterruptedIOException
but was:
java.net.ConnectException: Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8090
and this was my output.
from feign.
Looks like the url http://localhost:8090
is not working. Try using https://httpbin.org/delay/
instead.
from feign.
@Bean(name = "baseOkHttpClient")
public OkHttpClient baseOkHttpClient() {
return new OkHttpClient.Builder()
.readTimeout(500, TimeUnit.MILLISECONDS)
.connectTimeout(500, TimeUnit.MILLISECONDS)
.callTimeout(100,TimeUnit.MILLISECONDS)
.build();
}
@Bean(name = "cdpOkHttpClient")
public OkHttpClient cdpOkHttpClient(@Qualifier("baseOkHttpClient") OkHttpClient okHttpClient) {
return okHttpClient.newBuilder()
.readTimeout(1000, TimeUnit.MILLISECONDS)
.connectTimeout(1000, TimeUnit.MILLISECONDS)
.callTimeout(10,TimeUnit.MILLISECONDS)
.build();
}
Now this is my config for okhttp
@Bean
CdpDao feignBuilder(@Qualifier("cdpOkHttpClient") OkHttpClient cdpOkHttpClient) {
return Feign
.builder()
.client(new feign.okhttp.OkHttpClient(cdpOkHttpClient))
.encoder(new SpringEncoder(messageConverters))
.decoder(new SpringDecoder(messageConverters))
.retryer(new CDPRetryerConfig())
.target(CdpDao.class, "https://httpbin.org/delay");
}
And my config for Feign Client bean
@Test
void testCallTimeout(){
long start = System.currentTimeMillis();
Throwable throwable = catchThrowable(()-> cdpDao.getFeatures("3"));
long end = System.currentTimeMillis();
long start1 = System.currentTimeMillis();
Throwable throwable1 = catchThrowable(() -> cdpDao.getFeatures("4"));
long end1 = System.currentTimeMillis();
log.info("elasped time {}", end-start);
log.info("elasped second time {}", end1-start1);
assertThat(throwable1.getCause()).isInstanceOf(InterruptedIOException.class);
}
2024-03-15T14:47:01.410+05:30 INFO 39192 --- [ main] : Feign retry attempt 1 due to timeout executing POST https://httpbin.org/delay/
2024-03-15T14:47:01.475+05:30 INFO 39192 --- [ main] : Feign retry attempt 2 due to timeout executing POST https://httpbin.org/delay/
2024-03-15T14:47:01.590+05:30 INFO 39192 --- [ main] : Feign retry attempt 3 due to timeout executing POST https://httpbin.org/delay/
2024-03-15T14:47:01.590+05:30 INFO 39192 --- [ main] : elasped time 317
2024-03-15T14:47:01.590+05:30 INFO 39192 --- [ main] : elasped second time 194
I have a retryer setup at 1000 ms of read or connect Timeout and my call timeout is 10 ms as per the above config. So my retryer should not have been called and call timeout should have been executed. (But I am still getting the InterruptedIoException as expected and my test case is successful).
from feign.
@PostMapping(value = "/v1/readFeatureValues",
headers = {"X-USER-ID: {userAccountId}", "Content-Type: application/json"})
CdpResponseDto getFeatures(@RequestHeader("X-USER-ID") String userAccountId,
@RequestBody CdpRequestDto cdpRequestDto);
Here postmapping is not working also and I want to replace it with RequestLine which does not support using more parameters with RequestBody.
Can you help on this.
from feign.
You can find how to do it in documentation https://github.com/OpenFeign/feign
from feign.
Related Issues (20)
- feign-micrometer: No client tag on MicrometerObservation meters
- Add documentation/exception for required java compiler flag "parameters"
- Even using OptionalDecoder I get an exception on 404 HOT 1
- Builder breaks resilience4j 1.7.x (latest java <17 compatible release) HOT 3
- @PatchMapping and @DeleteMapping insert unwanted slash when using url and specific value HOT 3
- The current code can only get the thread pool of its own httpclient. How to get the thread pool used in feign.httpclient.ApacheHttpClient instead? HOT 1
- Final fields of payload object are not recognized when executing POST form-urlencoded HOT 1
- How to use AsyncCient for non blocking requests? HOT 3
- Intercepted values are preserved from previous request on retry
- Could we move to next Spring? HOT 1
- Is the response.body().asInputStream loads whole file into memory?
- Cannot get a response body from FeignException HOT 1
- Feign observation span is not put in "scope" - loggers do not see current spanId
- What was the need to do it in such a way?
- jdk http2client should not cache the InputStream HOT 1
- Resolving response protocol version using OkHttp3 fails due to OkHttp3.Protocoll class .toString() method not returning name of class HOT 1
- Observable FeignContext with immutable Headers
- AsyncApacheHttp5Client : Proposal to make creating the callback FutureCallback<SimpleHttpResponse>> through a public factory method
- Take ProxyMethod's "argv" into RequestTemplate
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from feign.