最近项目中 spring cloud 用到http请求,使用feign,配置okhttp,打算配置一下就直接使用,不过在压测与调优过程中遇到一些没有预测到的问题,附上排查与解析结
yml、pom配置
feign: client: config: default: connectTimeout: 2000 readTimeout: 3000 loggerLevel: FULL httpclient: enabled: false # 关闭 httpclient okhttp: enabled: true # 开启 okhttp
<!-- feign-okhttp --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> <version>4.2.2</version> <!-- <exclusions> --> <!-- 根据情况 --> <!-- </exclusions> --> </dependency>
配置后,http请求没问题
但在并发量100的场景下压测时,出现每个http请求相应时间达到100ms的情况,排查下游服务的反应时间很快,推测大量时间消耗在feign发起http请求
怀疑okHttp的负载均衡可能有问题,我的服务发现使用consul,也很好奇okhttp是怎么实现的负载均衡,继续排查:
配置feign配置类
@Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) throws NoSuchAlgorithmException, KeyManagementException { SSLContext ctx = SSLContext.getInstance("SSL"); X509TrustManager tm = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[]{tm}, null); return new LoadBalancerFeignClient(new Client.Default(ctx.getSocketFactory(), (hostname, session) -> { // TODO Auto-generated method stub return true; }) , cachingFactory, clientFactory); }
这里配置使用 LoadBalancerFeignClient,配置后,100并发下效果仍然不佳
经排查,代码中调用feign就耗费了100ms左右,只能深入看代码查原因,找一条响应时间长的case,看feign内部时间具体消耗在哪里:
这里clientName为consul配置的服务发现名,比如配置里
feign:
service:
order: test-name
在feignClient里配置解析后,这里直接拿到的就是"test-name",然后通过lbClient去请求consul集群,获取到ip+port
到这里,响应时间都很长,还需要继续深入
FeignLoadbalancer,在这里可以看出是否世纪使用了okhttp:
继续排查,发现发送http请求很快,但最简单的encode,decode出现时间长的情况
org.springframework.cloud.openfeign.support.SpringDecoder
问题在这里,每次使用http请求,都new HttpMessageConverterExtractor,费时费力,导致并发量高时load、响应时间都高了
解决方案
在feign配置类里,指定decoder,不要每次new
@Bean public Decoder feignDecoder() { ObjectMapper mapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .registerModule(new Jdk8Module()) //fix LocalDateTime 无法序列化问题 .registerModule(new JavaTimeModule()); return new JacksonDecoder(mapper); }
重新验证,100并发下响应时间降低到20ms,接近下游服务响应时间,http请求已不是瓶颈