Java 使用SpringREST模板,创建的连接太多或速度太慢

Java 使用SpringREST模板,创建的连接太多或速度太慢,java,spring,rest,http,apache-httpcomponents,Java,Spring,Rest,Http,Apache Httpcomponents,我有一个非常快速的RESTful服务。我正在本地主机上测试它。客户端正在使用SpringREST模板。我从一个天真的方法开始: RestTemplate restTemplate = new RestTemplate(Collections.singletonList(new GsonHttpMessageConverter())); Result result = restTemplate.postForObject(url, payload, Result.class); 当我提出很多这

我有一个非常快速的RESTful服务。我正在本地主机上测试它。客户端正在使用SpringREST模板。我从一个天真的方法开始:

RestTemplate restTemplate = new RestTemplate(Collections.singletonList(new GsonHttpMessageConverter()));

Result result = restTemplate.postForObject(url, payload, Result.class);
当我提出很多这样的请求时,我得到了以下例外:

Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8080/myservice":No buffer space available (maximum connections reached?): connect; nested exception is java.net.SocketException: No buffer space available (maximum connections reached?): connect
这是由于连接未关闭并挂起在TIME\U WAIT状态造成的。当临时端口耗尽时,异常开始发生。然后执行等待端口再次空闲。我看到了长时间休息的最佳表现。我得到的速度几乎是我所需要的,但当然,这些时间等待连接并不好。在Linux(Ubuntu14)和Windows(7)上都进行了测试,由于端口范围不同,在不同时间的测试结果相似

为了解决这个问题,我尝试使用ApacheHTTP组件库中带有HttpClientBuilder的HttpClient

RestTemplate restTemplate = new RestTemplate(Collections.singletonList(new GsonHttpMessageConverter()));
HttpClient httpClient = HttpClientBuilder.create()
        .setMaxConnTotal(TOTAL)
        .setMaxConnPerRoute(PER_ROUTE)
        .build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));

Result result = restTemplate.postForObject(url, payload, Result.class);
对于这个客户,我看不到任何例外。客户端现在只使用数量非常有限的临时端口。但无论我使用什么设置(总路线和每路线),我都无法获得所需的性能

使用
netstat
命令,我发现与服务器的连接不多。我尝试将数字设置为数千,但客户似乎从未使用过那么多

在不打开太多连接的情况下,我是否可以做些什么来提高性能


更新:我已尝试将总连接数和每条路由连接数设置为5000和2500,但看起来客户端创建的连接数仍不超过100个(从
netstat-n | wc-l
判断)。REST服务使用JAX-RS实现,并在Jetty上运行


更新2:我现在已经使用一些内存设置对服务器进行了调优,并且获得了非常好的吞吐量。这种简单的方法仍然要快一点,但我认为这只是客户端池的一点开销。

实际上Spring Boot并没有泄漏连接。这里您看到的是Linux内核(以及所有主要操作系统)的标准行为。从机器上关闭的所有插座在一段时间内进入
TIME\u WAIT
状态。这是为了防止使用该临时端口的下一个套接字接收实际用于该端口上的前一个套接字的数据包。您看到的这两种方法之间的差异是每种方法都采用连接池方法的结果

更具体地说,
restemplate
默认情况下不使用连接池。这意味着每次rest调用都会打开一个新的本地临时端口和到服务器的新连接。如果您的服务非常快,它将在任何时候都通过其可用的本地端口范围。使用Apache
HttpClient
,您可以利用连接池。这将阻止应用程序看到您描述的问题。然而,考虑到您的服务的响应速度比Linux内核从
TIME\u WAIT
中取出套接字的速度要快,连接池将使您的客户端变得更慢,无论您做什么(如果它没有减慢任何速度,那么您将再次耗尽本地临时端口)

虽然在Linux内核中启用TCP重用是可能的,但它可能会变得危险(数据包可能会延迟,并且您可能会让临时端口接收到他们不理解的随机数据包,这可能会导致各种问题)。这里的解决方案是使用第二个示例中的连接池,使用足够多的连接池以实现接近您所期望的性能


为了帮助您调整连接池,您需要调整
maxconnproute
maxconntottal
参数
maxConnPerRoute
限制将连接到单个IP:端口对的连接数,
maxTotal
限制将打开的总连接数。在您的情况下,由于所有请求似乎都是在同一个位置发出的,您可以将它们设置为相同的(高)值。

我将尝试重新表述这个问题(不要“泄漏”)。有什么方法可以设置apache客户端使用更大的池吗?通过检查netstat,我认为无论我使用什么设置,它都使用了大约100个端口。我将看看是否可以对服务器做些什么。您当前在
TOTAL
PER_ROUTE
中使用了哪些值?这些是操作连接池大小的设置,其中
PER_ROUTE
限制单个IP:端口对的连接数,并且
TOTAL
限制连接总数。我现在正处于一个所有操作都非常快的时刻。您能否更新您的答案,解释这两个参数并提及任何可能的开销?这样它对其他人会更有用。@sm4很高兴你把它解决了。我更新了我的答案,简要解释了
maxConnTotal
maxConnPerRoute
,根据,
RestTemplate
使用
ClientHttpPrequestFactory
,它委托给Apache
HttpClient
,但不能使用连接池……为什么……错误“没有可用的缓冲区空间”与系统的关联性更强,与代码的关联性更小。等待连接的时间很长,这表明连接不是池连接。不要调整“等待时间”设置,这将导致比解决问题更多的问题()。没有可用缓冲区空间的常见原因是损坏的网卡或Linux上的一个小wmem_max。在套接字(IP四倍)用完之前,您应该能够在同一端口上创建至少50k到60k到一个远程主机的连接。