From 049c3b3aa295e374a8160ea6ab230043945077b5 Mon Sep 17 00:00:00 2001 From: chentianming Date: Fri, 7 Oct 2022 16:44:32 +0800 Subject: [PATCH] support simple timeout config --- README.md | 31 ++++--- README_EN.md | 11 ++- pom.xml | 6 +- .../boot/config/GlobalTimeoutProperty.java | 32 ++++++++ .../boot/config/RetrofitProperties.java | 6 ++ .../retrofit/spring/boot/core/Constants.java | 4 +- .../spring/boot/core/RetrofitClient.java | 38 ++++++++- .../spring/boot/core/RetrofitFactoryBean.java | 37 +++++++-- .../boot/core/SourceOkHttpClientRegistry.java | 3 +- .../CustomSourceOkHttpClientRegistrar.java | 13 +-- .../spring/boot/test/timeout/TimeoutTest.java | 80 +++++++++++++++++++ .../boot/test/timeout/TimeoutTestApi.java | 24 ++++++ src/test/resources/application.yml | 12 +++ src/test/resources/default-config.yml | 12 +++ 14 files changed, 272 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/GlobalTimeoutProperty.java create mode 100644 src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/timeout/TimeoutTest.java create mode 100644 src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/timeout/TimeoutTestApi.java diff --git a/README.md b/README.md index 193f9df..286b015 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ gitee项目地址:[https://gitee.com/lianjiatech/retrofit-spring-boot-starter] com.github.lianjiatech retrofit-spring-boot-starter - 2.3.7 + 2.3.8 ``` @@ -143,6 +143,17 @@ retrofit: - response_status_not_2xx - occur_io_exception + # 全局超时时间配置 + global-timeout: + # 全局读取超时时间 + read-timeout-ms: 10000 + # 全局写入超时时间 + write-timeout-ms: 10000 + # 全局连接超时时间 + connect-timeout-ms: 10000 + # 全局完整调用超时时间 + call-timeout-ms: 0 + # 熔断降级配置 degrade: # 熔断降级类型。默认none,表示不启用熔断降级 @@ -168,8 +179,15 @@ retrofit: ## 高级功能 +### 超时时间配置 + +如果仅仅需要修改`OkHttpClient`的超时时间,可以通过`@RetrofitClient`相关字段修改,或者全局超时配置修改。 + + ### 自定义OkHttpClient +If you need to modify other configuration of `OkHttpClient`, you can do it by customizing `OkHttpClient`, the steps are as follows: + 1. 实现`SourceOkHttpClientRegistrar`接口,调用`SourceOkHttpClientRegistry#register()`方法注册`OkHttpClient`。 ```java @@ -180,17 +198,6 @@ retrofit: @Override public void register(SourceOkHttpClientRegistry registry) { - // 替换默认的SourceOkHttpClient,可以用来修改全局OkhttpClient设置 - registry.register(Constants.DEFAULT_SOURCE_OK_HTTP_CLIENT, new OkHttpClient.Builder() - .connectTimeout(Duration.ofSeconds(5)) - .writeTimeout(Duration.ofSeconds(5)) - .readTimeout(Duration.ofSeconds(5)) - .addInterceptor(chain -> { - log.info("============replace default SourceOkHttpClient============="); - return chain.proceed(chain.request()); - }) - .build()); - // 添加testSourceOkHttpClient registry.register("testSourceOkHttpClient", new OkHttpClient.Builder() .connectTimeout(Duration.ofSeconds(3)) diff --git a/README_EN.md b/README_EN.md index 1a97ab1..aa32a0d 100644 --- a/README_EN.md +++ b/README_EN.md @@ -37,7 +37,7 @@ com.github.lianjiatech retrofit-spring-boot-starter - 2.3.7 + 2.3.8 ``` @@ -118,6 +118,11 @@ retrofit: - response_status_not_2xx - occur_io_exception + global-timeout: + read-timeout-ms: 10000 + write-timeout-ms: 10000 + connect-timeout-ms: 10000 + call-timeout-ms: 0 degrade: degrade-type: none global-sentinel-degrade: @@ -136,6 +141,10 @@ retrofit: ## Advanced Features +### Timeout config + +If you only need to modify the timeout time of `OkHttpClient`, you can modify it through the relevant fields of `@RetrofitClient`, or modify the global timeout configuration. + ### Customize OkHttpClient 1. Implement the `SourceOkHttpClientRegistrar` interface and call the `SourceOkHttpClientRegistry#register()` method to register the `OkHttpClient`. diff --git a/pom.xml b/pom.xml index 1391fb0..e6c1917 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.lianjiatech retrofit-spring-boot-starter - 2.3.7 + 2.3.8 retrofit-spring-boot-starter retrofit-spring-boot-starter @@ -41,7 +41,7 @@ 4.13.1 1.3.2 1.7.25 - 2.0.9.RELEASE + 2.2.11.RELEASE 3.14.9 1.6.3 2.12.6.1 @@ -58,7 +58,7 @@ org.springframework.cloud spring-cloud-commons - 2.0.4.RELEASE + 2.2.9.RELEASE provided diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/GlobalTimeoutProperty.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/GlobalTimeoutProperty.java new file mode 100644 index 0000000..3a0268f --- /dev/null +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/GlobalTimeoutProperty.java @@ -0,0 +1,32 @@ +package com.github.lianjiatech.retrofit.spring.boot.config; + +import lombok.Data; + +/** + * 只有在@RetrofitClient.sourceOkHttpClient为NO_SOURCE_OK_HTTP_CLIENT时才有效 + * @author 陈添明 + * @since 2022/10/7 4:23 下午 + */ +@Data +public class GlobalTimeoutProperty { + + /** + * 全局连接超时时间 + */ + private int connectTimeoutMs = 10_000; + + /** + * 全局读取超时时间 + */ + private int readTimeoutMs = 10_000; + + /** + * 全局写入超时时间 + */ + private int writeTimeoutMs = 10_000; + + /** + * 全局完整调用超时时间 + */ + private int callTimeoutMs = 0; +} diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitProperties.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitProperties.java index 9cc71dd..2ecab59 100644 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitProperties.java +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitProperties.java @@ -42,6 +42,12 @@ public class RetrofitProperties { @NestedConfigurationProperty private GlobalLogProperty globalLog = new GlobalLogProperty(); + /** + * 全局超时配置 + */ + @NestedConfigurationProperty + private GlobalTimeoutProperty globalTimeout = new GlobalTimeoutProperty(); + /** * 全局转换器工厂,转换器实例优先从Spring容器获取,如果没有获取到,则反射创建。 * global converter factories, The converter instance is first obtained from the Spring container. If it is not obtained, it is created by reflection. diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/Constants.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/Constants.java index e283242..b4abb30 100644 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/Constants.java +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/Constants.java @@ -8,7 +8,7 @@ public interface Constants { String STR_EMPTY = ""; - String DEFAULT_SOURCE_OK_HTTP_CLIENT = "defaultSourceOkHttpClient"; + String NO_SOURCE_OK_HTTP_CLIENT = ""; String SPH_U_CLASS_NAME = "com.alibaba.csp.sentinel.SphU"; @@ -19,4 +19,6 @@ public interface Constants { String RETROFIT = "retrofit"; String DEFAULT_CIRCUIT_BREAKER_CONFIG = "defaultCircuitBreakerConfig"; + + int INVALID_TIMEOUT_VALUE = -1; } diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitClient.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitClient.java index f8d78c5..93bc8e1 100644 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitClient.java +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitClient.java @@ -92,6 +92,42 @@ /** * 原始OkHttpClient,根据该名称到#{@link SourceOkHttpClientRegistry}查找对应的OkHttpClient来构建当前接口的OkhttpClient。 */ - String sourceOkHttpClient() default Constants.DEFAULT_SOURCE_OK_HTTP_CLIENT; + String sourceOkHttpClient() default Constants.NO_SOURCE_OK_HTTP_CLIENT; + + + /*===============以下属性只有在sourceOkHttpClient为NO_SOURCE_OK_HTTP_CLIENT时才有效=================*/ + + /** + * Sets the default connect timeout for new connections. A value of 0 means no timeout, + * otherwise values must be between 1 and Integer.MAX_VALUE when converted to milliseconds. + * If it is configured as -1, the global default configuration is used. + * + */ + int connectTimeoutMs() default Constants.INVALID_TIMEOUT_VALUE; + + /** + * Sets the default read timeout for new connections. A value of 0 means no timeout, + * otherwise values must be between 1 and Integer.MAX_VALUE when converted to milliseconds. + * If it is configured as -1, the global default configuration is used. + * + */ + int readTimeoutMs() default Constants.INVALID_TIMEOUT_VALUE; + + /** + * Sets the default write timeout for new connections. A value of 0 means no timeout, + * otherwise values must be between 1 and Integer.MAX_VALUE when converted to milliseconds. + * If it is configured as -1, the global default configuration is used. + * + */ + int writeTimeoutMs() default Constants.INVALID_TIMEOUT_VALUE; + + + /** + * Sets the default timeout for complete calls. A value of 0 means no timeout, + * otherwise values must be between 1 and Integer.MAX_VALUE when converted to milliseconds. + * If it is configured as -1, the global default configuration is used. + * + */ + int callTimeoutMs() default Constants.INVALID_TIMEOUT_VALUE; } diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java index 482269d..711ca2f 100644 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java @@ -8,7 +8,10 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.TimeUnit; +import com.github.lianjiatech.retrofit.spring.boot.config.GlobalTimeoutProperty; +import com.github.lianjiatech.retrofit.spring.boot.config.RetrofitProperties; import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; import org.springframework.context.ApplicationContext; @@ -90,9 +93,32 @@ public boolean isSingleton() { private OkHttpClient createOkHttpClient() { RetrofitClient retrofitClient = AnnotatedElementUtils.findMergedAnnotation(retrofitInterface, RetrofitClient.class); - OkHttpClient sourceOkHttpClient = retrofitConfigBean.getSourceOkHttpClientRegistry() - .get(Objects.requireNonNull(retrofitClient).sourceOkHttpClient()); - OkHttpClient.Builder okHttpClientBuilder = sourceOkHttpClient.newBuilder(); + + OkHttpClient.Builder okHttpClientBuilder; + if (Constants.NO_SOURCE_OK_HTTP_CLIENT.equals(Objects.requireNonNull(retrofitClient).sourceOkHttpClient())) { + // 使用默认超时时间创建OkHttpClient + GlobalTimeoutProperty globalTimeout = retrofitConfigBean.getRetrofitProperties().getGlobalTimeout(); + + int connectTimeoutMs = retrofitClient.connectTimeoutMs() == Constants.INVALID_TIMEOUT_VALUE + ? globalTimeout.getConnectTimeoutMs() : retrofitClient.connectTimeoutMs(); + int readTimeoutMs = retrofitClient.readTimeoutMs() == Constants.INVALID_TIMEOUT_VALUE + ? globalTimeout.getReadTimeoutMs() : retrofitClient.readTimeoutMs(); + int writeTimeoutMs = retrofitClient.writeTimeoutMs() == Constants.INVALID_TIMEOUT_VALUE + ? globalTimeout.getWriteTimeoutMs() : retrofitClient.writeTimeoutMs(); + int callTimeoutMs = retrofitClient.callTimeoutMs() == Constants.INVALID_TIMEOUT_VALUE + ? globalTimeout.getCallTimeoutMs() : retrofitClient.callTimeoutMs(); + + okHttpClientBuilder = new OkHttpClient.Builder() + .connectTimeout(connectTimeoutMs, TimeUnit.MILLISECONDS) + .readTimeout(readTimeoutMs, TimeUnit.MILLISECONDS) + .writeTimeout(writeTimeoutMs, TimeUnit.MILLISECONDS) + .callTimeout(callTimeoutMs, TimeUnit.MILLISECONDS); + } else { + OkHttpClient sourceOkHttpClient = retrofitConfigBean.getSourceOkHttpClientRegistry() + .get(retrofitClient.sourceOkHttpClient()); + okHttpClientBuilder = sourceOkHttpClient.newBuilder(); + } + if (isEnableDegrade(retrofitInterface)) { okHttpClientBuilder.addInterceptor(retrofitConfigBean.getRetrofitDegrade()); } @@ -159,7 +185,8 @@ private List findInterceptorByAnnotation() { private Retrofit createRetrofit() { RetrofitClient retrofitClient = AnnotatedElementUtils.findMergedAnnotation(retrofitInterface, RetrofitClient.class); - String baseUrl = RetrofitUtils.convertBaseUrl(retrofitClient, retrofitClient.baseUrl(), environment); + String baseUrl = RetrofitUtils.convertBaseUrl(retrofitClient, Objects.requireNonNull(retrofitClient).baseUrl(), + environment); OkHttpClient client = createOkHttpClient(); Retrofit.Builder retrofitBuilder = new Retrofit.Builder() @@ -186,7 +213,7 @@ private Retrofit createRetrofit() { converterFactories.addAll(Arrays.asList(retrofitClient.converterFactories())); converterFactories.addAll(Arrays.asList(retrofitConfigBean.getGlobalConverterFactoryClasses())); converterFactories.forEach(converterFactoryClass -> retrofitBuilder - .addConverterFactory(AppContextUtils.getBeanOrNew(applicationContext, converterFactoryClass))); + .addConverterFactory(AppContextUtils.getBeanOrNew(applicationContext, converterFactoryClass))); return retrofitBuilder.build(); } diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/SourceOkHttpClientRegistry.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/SourceOkHttpClientRegistry.java index 72845b2..89e1e40 100644 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/SourceOkHttpClientRegistry.java +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/SourceOkHttpClientRegistry.java @@ -23,8 +23,7 @@ public class SourceOkHttpClientRegistry { public SourceOkHttpClientRegistry(List registrars) { this.registrars = registrars; - this.okHttpClientMap = new HashMap<>(8); - this.okHttpClientMap.put(Constants.DEFAULT_SOURCE_OK_HTTP_CLIENT, new OkHttpClient()); + this.okHttpClientMap = new HashMap<>(4); } @PostConstruct diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/custom/okhttp/CustomSourceOkHttpClientRegistrar.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/custom/okhttp/CustomSourceOkHttpClientRegistrar.java index a7f6d5c..9edebf1 100644 --- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/custom/okhttp/CustomSourceOkHttpClientRegistrar.java +++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/custom/okhttp/CustomSourceOkHttpClientRegistrar.java @@ -22,18 +22,7 @@ public class CustomSourceOkHttpClientRegistrar implements SourceOkHttpClientRegi @Override public void register(SourceOkHttpClientRegistry registry) { - // 替换默认的SourceOkHttpClient,可以用来修改全局OkhttpClient设置 - registry.register(Constants.DEFAULT_SOURCE_OK_HTTP_CLIENT, new OkHttpClient.Builder() - .connectTimeout(Duration.ofSeconds(5)) - .writeTimeout(Duration.ofSeconds(5)) - .readTimeout(Duration.ofSeconds(5)) - .addInterceptor(chain -> { - log.info("============replace default SourceOkHttpClient============="); - return chain.proceed(chain.request()); - }) - .build()); - - // 添加testSourceOkHttpClient + // 注册testSourceOkHttpClient registry.register("testSourceOkHttpClient", new OkHttpClient.Builder() .connectTimeout(Duration.ofSeconds(3)) .writeTimeout(Duration.ofSeconds(3)) diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/timeout/TimeoutTest.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/timeout/TimeoutTest.java new file mode 100644 index 0000000..6497940 --- /dev/null +++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/timeout/TimeoutTest.java @@ -0,0 +1,80 @@ +package com.github.lianjiatech.retrofit.spring.boot.test.timeout; + +import java.io.IOException; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.lianjiatech.retrofit.spring.boot.test.RetrofitTestApplication; +import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person; +import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; + +/** + * @author 陈添明 + */ +@SpringBootTest(classes = RetrofitTestApplication.class) +@RunWith(SpringRunner.class) +public class TimeoutTest { + + @Autowired + private TimeoutTestApi timeoutTestApi; + + private MockWebServer server; + + private static final ObjectMapper objectMapper = + new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL); + + @Before + public void before() throws IOException { + System.out.println("=========开启MockWebServer==========="); + server = new MockWebServer(); + server.start(8080); + + } + + @After + public void after() throws IOException { + System.out.println("=========关闭MockWebServer==========="); + server.close(); + } + + @Test + public void test() throws JsonProcessingException { + // mock + Person mockPerson = new Person().setId(1L) + .setName("test") + .setAge(10); + Result mockResult = new Result<>() + .setCode(0) + .setMsg("ok") + .setData(mockPerson); + MockResponse response = new MockResponse() + .setResponseCode(200) + .addHeader("Content-Type", "application/json; charset=utf-8") + .addHeader("Cache-Control", "no-cache") + .setBody(objectMapper.writeValueAsString(mockResult)); + server.enqueue(response); + + // http check + Result person = timeoutTestApi.getPerson(1L); + Person data = person.getData(); + Assert.assertNotNull(data); + Assert.assertEquals("test", data.getName()); + Assert.assertEquals(10, data.getAge().intValue()); + } + +} diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/timeout/TimeoutTestApi.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/timeout/TimeoutTestApi.java new file mode 100644 index 0000000..a654cf2 --- /dev/null +++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/timeout/TimeoutTestApi.java @@ -0,0 +1,24 @@ +package com.github.lianjiatech.retrofit.spring.boot.test.timeout; + +import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitClient; +import com.github.lianjiatech.retrofit.spring.boot.log.LogStrategy; +import com.github.lianjiatech.retrofit.spring.boot.log.Logging; +import com.github.lianjiatech.retrofit.spring.boot.test.InvalidRespErrorDecoder; +import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person; + +import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Query; + +/** + * @author 陈添明 + */ +@Logging(logStrategy = LogStrategy.BODY) +@RetrofitClient(baseUrl = "${test.baseUrl}", readTimeoutMs = 2000) +public interface TimeoutTestApi { + + @GET("person") + Result getPerson(@Query("id") Long id); +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index bdd7512..ea3ca37 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -30,6 +30,18 @@ retrofit: - response_status_not_2xx - occur_io_exception + # 全局超时时间配置 + global-timeout: + # 全局读取超时时间 + read-timeout-ms: 10000 + # 全局写入超时时间 + write-timeout-ms: 10000 + # 全局连接超时时间 + connect-timeout-ms: 10000 + # 全局完整调用超时时间 + call-timeout-ms: 0 + + # 熔断降级配置 degrade: # 熔断降级类型。默认none,表示不启用熔断降级 diff --git a/src/test/resources/default-config.yml b/src/test/resources/default-config.yml index a7b3e09..dfc5193 100644 --- a/src/test/resources/default-config.yml +++ b/src/test/resources/default-config.yml @@ -27,6 +27,18 @@ retrofit: - response_status_not_2xx - occur_io_exception + # 全局超时时间配置 + global-timeout: + # 全局读取超时时间 + read-timeout-ms: 10000 + # 全局写入超时时间 + write-timeout-ms: 10000 + # 全局连接超时时间 + connect-timeout-ms: 10000 + # 全局完整调用超时时间 + call-timeout-ms: 0 + + # 熔断降级配置 degrade: # 熔断降级类型。默认none,表示不启用熔断降级