35

I have a RESTful service that works very fast. I am testing it on localhost. The client is using Spring REST template. I started by using a naive approach:

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

Result result = restTemplate.postForObject(url, payload, Result.class);

When I make a lot of these requests, I am getting the following exception:

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

This is caused by connections not being closed and hanging in TIME_WAIT state. The exception starts happening when the ephemeral ports are exhausted. Then the execution waits for the ports to be free again. I am seeing peak performance with long breaks. The rate I am getting is almost what I need, but of course, these TIME_WAIT connections are not good. Tested both on Linux (Ubuntu 14) and Windows (7), similar results at different times due to different ranges of the ports.

To fix this, I tried using an HttpClient with HttpClientBuilder from Apache Http Components library.

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);

With this client, I see no exceptions. The client is now using only a very limited number of ephemeral ports. But whatever settings I use (TOTAL and PER_ROUTE), I can't get the performance I need.

Using the netstat command, I see that there are not many connections done to the server. I tried setting the numbers to several thousands, but it seems the client never uses that much.

Is there anything I can do to improve the performance, without opening too many connections?


UPDATE: I've tried setting number of total and per route connections to 5000 and 2500 but it still looks like the client is not creating more than a hundred (judging from netstat -n | wc -l). The REST service is implemented using JAX-RS and running on Jetty.

UPDATE2: I have now tuned the server with some memory settings and I am getting really good throughput. The naive approach is still a bit faster, but I think it's just a little overhead of the pooling on client side.

MartinTeeVarga
  • 10,478
  • 12
  • 61
  • 98
  • 1
    The error "No buffer space available" is more related with your system, less with your code. Lots of TIME_WAIT connections indicate, the connections are not pooled. Do not tweak the TIME_WAIT settings, will cause more problems than it solves (http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html). The usual causes for no buffer space available are broken network cards or a small wmem_max on Linux. You should be able to create at least 50k to 60k connections to one remote host on the same port before you run out of sockets (IP quadruples). – mp911de Aug 08 '15 at 14:11

1 Answers1

48

Actually Spring Boot is not leaking connections. What you're seeing here is standard behavior of the Linux kernel (and every major OS). All sockets that are closed from the machine go to a TIME_WAIT state for some duration of time. This is to prevent the next socket that uses that ephemeral port from receiving packets that were actually intended for the previous socket on that port. The difference you're seeing between the two is a result of the connection pooling approaches each one takes.

More specifically, RestTemplate does not use connection pooling by default. This means every rest call opens a new local ephemeral port and a new connection to the server. If your service is very fast, it will blow through its available local port range in no time at all. With the Apache HttpClient, you are taking advantage of connection pooling. This will prevent your application from seeing the problem that you described. However, given that your service is able to respond faster than the Linux kernel is taking sockets out of TIME_WAIT, connection pooling will make your client slower no matter what you do (if it didn't slow anything down - then you'd run out of local ephemeral ports again).

While it's possible to enable TCP reuse in the Linux kernel, it's can get dangerous (packets can get delayed and you could get ephemeral ports receiving random packets they don't understand which could cause all kinds of problems). The solution here is to use connection pooling as you have in the second example, with sufficiently high numbers to achieve close to the performance you're looking for.

To help you tune your connection pool, you'll want to tweak the maxConnPerRoute and maxConnTotal parameters. maxConnPerRoute limits the number of connections that will be made to a single IP:Port pair, and maxTotal limits the number of total connections that will ever be opened. In your case, since it appears all requests are made to the same location, you could set them to the same (high) value.

Colin M
  • 13,010
  • 3
  • 38
  • 58
  • 1
    I will try to rephrase the question (without "leaking"). Is there any way I can set the apache client to use a bigger pool? By checking netstat, I think it's using maybe hundred ports, no matter what setting I use. I will see if I can do something about the server. – MartinTeeVarga Aug 08 '15 at 12:20
  • What values are you currently using in `TOTAL` and `PER_ROUTE`? Those are the settings to manipulate connection pool sizes, where `PER_ROUTE` limits the number of connections to a single IP:Port pair, and `TOTAL` limits, well, the total number of connections – Colin M Aug 08 '15 at 12:23
  • 3
    I am now at a point when everything works quite fast. Could you update your answer with explanation of those two parameters + mention of any possible overhead? That way it would be more useful to other people. – MartinTeeVarga Aug 10 '15 at 08:03
  • 4
    @sm4 glad you got it worked out. I updated my answer with brief explanations of `maxConnTotal` and `maxConnPerRoute` – Colin M Aug 10 '15 at 10:27
  • According to [here](https://stackoverflow.com/questions/31483874/resttemplate-vs-apache-http-client-for-production-code-in-spring-project), `RestTemplate` uses `ClientHttpRequestFactory`, which delegates to Apache `HttpClient`, but cannot use a connection pool....why... – WesternGun Mar 14 '19 at 15:42