我有一个需求,需要从外部系统获取用户数据。我通过 WebClient 实现的,作为声明性 HTTP 客户端的一部分。
有趣的是,在新的浏览器标签页中打开应用程序后,第一次请求返回了“Connection reset by peer”错误。
这个问题通过禁用对外请求的 keep-alive 解决了。
改造前的代码:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
@Configuration
public class HttpProxyConfiguration {
@Value("${tracker.url}")
private String trackerUrl;
@Bean
TrackerClient trackerClient(WebClient.Builder builder) {
var wc = builder.baseUrl(trackerUrl)
.build();
var wca = WebClientAdapter.forClient(wc);
return HttpServiceProxyFactory.builder()
.clientAdapter(wca)
.build()
.createClient(TrackerClient.class);
}
}
改造后的代码:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
import reactor.netty.http.client.HttpClient;
@Configuration
public class HttpProxyConfiguration {
@Value("${tracker.url}")
private String trackerUrl;
@Bean
TrackerClient trackerClient(WebClient.Builder builder) {
var httpClient = HttpClient.newConnection().keepAlive(false);
var reactorClientHttpConnector = new ReactorClientHttpConnector(httpClient);
var wc = builder.baseUrl(trackerUrl)
.clientConnector(reactorClientHttpConnector)
.build();
var wca = WebClientAdapter.forClient(wc);
return HttpServiceProxyFactory.builder()
.clientAdapter(wca)
.build()
.createClient(TrackerClient.class);
}
在查看了这个 GitHub Issue (https://github.com/reactor/reactor-netty/issues/388#issuecomment-704069492)后,似乎禁用连接池或禁用 keep-alive 并不推荐。
因此,我采用了该 GitHub Issue (https://github.com/reactor/reactor-netty/issues/1774#issuecomment-908066283)中提到的解决方案,设置了某些连接超时。
@Configuration
public class HttpProxyConfiguration {
@Value("${tracker.url}")
private String trackerUrl;
@Bean
TrackerClient trackerClient(WebClient.Builder builder) {
ConnectionProvider provider = ConnectionProvider.builder("fixed")
.maxConnections(500)
.maxIdleTime(Duration.ofSeconds(20))
.maxLifeTime(Duration.ofSeconds(60))
.pendingAcquireTimeout(Duration.ofSeconds(60))
.evictInBackground(Duration.ofSeconds(120)).build();
HttpClient httpClient = HttpClient.create(provider);
httpClient.warmup().block();
var reactorClientHttpConnector = new ReactorClientHttpConnector(httpClient);
var wc = builder.baseUrl(trackerUrl)
.clientConnector(reactorClientHttpConnector)
.build();
var wca = WebClientAdapter.forClient(wc);
return HttpServiceProxyFactory.builder()
.clientAdapter(wca)
.build()
.createClient(TrackerClient.class);
}
}
上述代码已经至少在生产环境中运行了两周。到目前为止,我还没有遇到任何错误。通过设置连接池,我希望能够提高应用程序的性能,特别是由于它经常需要向另一个服务发出大量请求以获取前端所需的所有数据。我计划继续长期监控这个解决方案,以评估其影响,并确定它是否确实提升了应用程序的性能。
欢迎关注公众号:SpringForAll社区(spring4all.com),专注分享关于Spring的一切!回复“加群”还可加入Spring技术交流群!
给大家推荐我们团队开发的Chrome插件:YouTube中文配音。如果您跟我们一样,热爱看国外的视频学习前沿知识或者其他内容,该插件可以很好的帮助您讲外语视频一键转化为中文视频,官网:https://www.youtube-dubbing.com/
END
回复关键词:加群