How to control the okHttpClient connections size?
Asked Answered
E

1

8

I am debugging an issue in my android app. I found the root cause is file descriptors went beyond the limit. After further investigation I found that the app has too many sockets open. I use OkHttpClient 2.5 for all of my network communication, thus I am wondering how should I limit my connection pool size. Below is my code snippet:

    OkHttpClient okHttpClient = new OkHttpClient().setConnectTimeout(TIMEOUT);
    ConnectionPool connectionPool = new ConnectionPool(MAX_IDLE_CONNECTIONS,
                                                       KEEP_ALIVE_DURATION_MS);
    okHttpClient.set(connectionPool);

    @RequireArgsConstructor
    public HttpEngineCallable implements Callable<IHttpResponse>
    {
      private final String url;

      public IHttpResponse call () throws Exception 
      {
         try
        {
           Request request = Request.Builder().url(url).build();
           Call call = okHttpClient.newCall(request);
           Response rawResponse = call.execute();
           return new OkHttpResponse(rawResponse);
        }
        catch (Exception e)
        {
           throw new IllegalStateException(e);
        }
    }  

    private final Function<IHttpResponse, T> httpResponseParser = new Function<IHttpResponse, T>()
    {
        @Nullable
        @Override
        public T apply(@Nullable IHttpResponse httpResponse)
        {
            if(httpResponse == null)
            {
               return null;
            }

            InputStream stream = httpResponse.getBody();
            JsonParser parser = null;
            T result = null;

            try
            {
               parser = jsonFactory.createParser(stream);
               result = strategy.parseData(parser);
            }
            catch (Exception e)
            {
               log.error("Unable to convert {} with {}.", stream, strategy, e);
            }
            finally
            {
               IOUtils.closeQuietly(parser);
               IOUtils.closeQuietly(stream);
            } 

          return result;
        }
     };

     Future<T> future = executorService.submit(new HttpEngineCallable(url));
     Future<V> finalFuture = Futures.transform(future, httpResponseParser, executorService);

     T result = timeoutExecutorService.submit(new Runnable() 
     {
        try
        { 
          T result = finalFuture.get(CLIENT_TIMEOUT, TIMEUNIT)
          if (result == null)
          { 
            notify onFailure listeners
          }
          else
          {
            notify onSuccess Listeners
          }
        }
        catch(Exception ex)
        {
          notify onFailure listeners
        }
     } 

So I have a few questions regarding this implementation:

  1. My CLIENT_TIMEOUT is shorter than OkHttp ConnectTimeout. If my finalFuture.get(CLINT_TIMEOUT, TIMEUNIT) throws timeout exception, would my finally block in the Parser Function still be executed? I am counting on it to close my connection.
  2. How can limits the size of my ConnectionPool? Is there way I can auto-recycle oldest connections if connection went beyond limit?
Eyeglass answered 4/3, 2016 at 23:30 Comment(3)
Re (1), finally blocks are always executed. That's what they're for.Keaton
@EJP I was worried about the following two situations to be exact 1.) finalFuture.get() times out on a shorter timeout. Since this get() forces the execution of the httpResponseParser.apply(), this part of execution just never happened. 2.) HttpEngineCallable.call() had exception happened. I am not sure the execution path would still reaches to the httpResponseParser.apply() part. Do you know if in both of above situations the execution path would still reach the httpResponseParser.apply() part that contains the finally block?Eyeglass
Look into this: #42949485Telpherage
T
13

We had a similar issue with too many open file descriptors crashing our app.

The problem was that we created one OkHttpClient per request. By default each OkHttpClient comes with its own connection pool, which of course blows up the number of connections/threads/file handles and prevents proper reuse in the pool.

We solved the problem by manually creating a global ConnectionPool in a singleton, and then passing that to the OkHttpClient.Builder object which builds the actual OkHttpClient.

...
builder.connectionPool(GLOBAL_CONNECTION_POOL);
OkHttpClient client = builder.build();
...

This still allows for per-request configuration using the OkHttpClient.Builder and makes sure all OkHttpClient instances are still using a common connection pool. We were then able to properly size the global connection pool.

Telpherage answered 22/3, 2017 at 10:39 Comment(1)
It would be so great if you could provide the code thoughBogard

© 2022 - 2024 — McMap. All rights reserved.