http接口超时方案
-
方案1:多个resttemplate,不同超时时间的使用不同的template,优点:简单,扩展起来复制粘贴,缺点:代码冗余,多个template占用内存不够优雅
-
方案2:单个resttemplate,调用过程中反射修改超时时间,优点:比较高端,缺点:可能有多线程问题,需要充分测试,反射影响一点点性能
-
方案3:使用OkHttpClient包装一层resttemplate,再使用OkHttpClient提供的拦截器,每次调用的时候拦截根据url修改超时时间,优点:优雅;使用RestTemplateInterceptor包装后,可以动态修改每个接口的超时时间。缺点:需要引用自己的resttemplate,autowire的地方需要修改。
dubbo接口超时方案
-
利用自带的dubbot的timeount属性设置超时时间:dubbo的超时时间可以设置在生产者和消费者,并且消费者可以覆盖生产者的配置,所以我们只需要关心消费者的配置
-
方法 > 接口 > 全局
-
同级别下消费 > 生产
-
不仅针对超时timeout属性,所有可配置属性都具备该优先级规则
-
有个小坑需要注意,在@Reference上设置的超时时间可能出现无效的情况,当存在多个@Reference配置时,程序启动的时候,会根据加载顺序进行加载并覆盖之前的配置,所以要想得到正确的配置,需要所有引用接口的地方的超时时间设置一样
http超时设置代码
1. 配置RestTemplate的bean
import com.f4.ts.org.manager.constants.NumberConstant;
import com.f4.ts.org.manager.interceptor.RestTemplateInterceptor;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.TimeUnit;
/**
* restful风格模板配置
*
* @author tengqy
* @create 2022-07-04 8:33
* @date 2022/07/05
*/
@Configuration
public class RestTemplateConfig {
/**
* restful风格模板拦截器
*/
@Autowired
private RestTemplateInterceptor restTemplateInterceptor;
/**
* 企业restful风格模板
*
* @return {@link RestTemplate}
*/
@Bean
public RestTemplate enterpriseRestTemplate() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(NumberConstant.NUM_INT_1000, TimeUnit.MILLISECONDS).readTimeout(NumberConstant.NUM_INT_1000, TimeUnit.MILLISECONDS)
.writeTimeout(NumberConstant.NUM_INT_1000, TimeUnit.MILLISECONDS)
.connectionPool(new ConnectionPool(NumberConstant.NUM_INT_50,
NumberConstant.NUM_INT_13, TimeUnit.MINUTES)).addInterceptor(restTemplateInterceptor);
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory(builder.build()));
return restTemplate;
}
}
2. 配置拦截器
import com.alibaba.fastjson.JSON;
import com.f4.ts.org.manager.config.TimeOutConfig;
import com.f4.ts.org.manager.constants.NumberConstant;
import com.f4.ts.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* restful风格模板拦截器
*
* @author tengqy
* @create 2022-07-04 8:35
* @date 2022/07/05
*/
@Component
@Slf4j
public class RestTemplateInterceptor implements Interceptor {
/**
* 时间配置
*/
@Autowired
private TimeOutConfig timeOutConfig;
/**
* 超时地图
*/
private static Map<String, List<Integer>> timeoutMap = new ConcurrentHashMap<>();
/**
* 最后一次
*/
private long lastTime;
/**
* 全部url
*/
private static final String DEFAULT_URL = "*";
/**
* 初始化
*/
@PostConstruct
public void init() {
lastTime = System.currentTimeMillis();
String timeoutCof = timeOutConfig.getTimeoutCof();
log.info("初始化http超时时间配置 {}", timeoutCof);
if (StringUtils.isNotBlank(timeoutCof)) {
String[] split = timeoutCof.split(";");
for (String s : split) {
String[] strings = s.split(",");
String connectionTimeout = "2000";
String readTimeout = "2000";
String writeTimeout = "2000";
if (strings.length == NumberConstant.NUM_INT_4) {
connectionTimeout = strings[NumberConstant.NUM_INT_1];
readTimeout = strings[NumberConstant.NUM_INT_2];
writeTimeout = strings[NumberConstant.NUM_INT_3];
}
timeoutMap.put(strings[0], Arrays.asList(Integer.valueOf(connectionTimeout), Integer.valueOf(readTimeout),
Integer.valueOf(writeTimeout)));
}
}
timeoutMap.put(DEFAULT_URL, Arrays.asList(NumberConstant.NUM_INT_2000, NumberConstant.NUM_INT_2000, NumberConstant.NUM_INT_2000));
log.info("初始化http超时时间配置结束 {}", JSON.toJSONString(timeoutMap));
}
/**
* 拦截
*
* @param chain 链
* @return {@link Response}
* @throws IOException ioexception
*/
@Override
public Response intercept(Chain chain) throws IOException {
if (System.currentTimeMillis() - lastTime > (long) NumberConstant.NUMBER_600000) {
init();
lastTime = System.currentTimeMillis();
}
Request request = chain.request();
String requestUrl = request.url().toString();
for (Map.Entry<String, List<Integer>> entry : timeoutMap.entrySet()) {
if (requestUrl.endsWith(entry.getKey())) {
return chain.withConnectTimeout(entry.getValue().get(NumberConstant.NUM_INT_0), TimeUnit.MILLISECONDS)
.withReadTimeout(entry.getValue().get(NumberConstant.NUM_INT_1), TimeUnit.MILLISECONDS)
.withWriteTimeout(entry.getValue().get(NumberConstant.NUM_INT_2), TimeUnit.MILLISECONDS)
.proceed(request);
}
}
List<Integer> defaultTime = timeoutMap.get(DEFAULT_URL);
return chain.withConnectTimeout(defaultTime.get(NumberConstant.NUM_INT_0), TimeUnit.MILLISECONDS)
.withReadTimeout(defaultTime.get(NumberConstant.NUM_INT_1), TimeUnit.MILLISECONDS)
.withWriteTimeout(defaultTime.get(NumberConstant.NUM_INT_2), TimeUnit.MILLISECONDS)
.proceed(request);
}
}
3. 动态配置接口的超时时间,可以配置到配置中心等地方
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 时间配置
*
* @author tengqy
* @create 2022-07-05 9:18
* @date 2022/07/05
*/
@Component
@Data
@ConfigurationProperties
public class TimeOutConfig {
/**
* 超时
*/
@Value("${http.timeout.conf:*,2000,2000,2000}")
private String timeoutCof;
/**
* 得到超时
*
* @return {@link String}
*/
public String getTimeoutCof() {
return timeoutCof;
}
/**
* 设置超时
*
* @param timeoutCof 超时咖啡
*/
public void setTimeoutCof(String timeoutCof) {
this.timeoutCof = timeoutCof;
}
}
超时时间配置如下
http:
timeout:
conf: url1;url2,1000,2000,2000
其中,如果不配置超时时间,则默认超时时间都是2s/2s/2s,否则配置了超时时间则按照
接口,a,b,c; 配置,其中a、b、c分别为建立连接超时时间、读超时时间、写超时时间