All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squareup.okhttp.ConnectionPool Maven / Gradle / Ivy

There is a newer version: 2.7.5
Show newest version
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.squareup.okhttp;

import com.squareup.okhttp.internal.Platform;
import com.squareup.okhttp.internal.Util;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Manages reuse of HTTP and SPDY connections for reduced network latency. HTTP
 * requests that share the same {@link com.squareup.okhttp.Address} may share a
 * {@link com.squareup.okhttp.Connection}. This class implements the policy of
 * which connections to keep open for future use.
 *
 * 

The {@link #getDefault() system-wide default} uses system properties for * tuning parameters: *

    *
  • {@code http.keepAlive} true if HTTP and SPDY connections should be * pooled at all. Default is true. *
  • {@code http.maxConnections} maximum number of idle connections to * each to keep in the pool. Default is 5. *
  • {@code http.keepAliveDuration} Time in milliseconds to keep the * connection alive in the pool before closing it. Default is 5 minutes. * This property isn't used by {@code HttpURLConnection}. *
* *

The default instance doesn't adjust its configuration as system * properties are changed. This assumes that the applications that set these * parameters do so before making HTTP connections, and that this class is * initialized lazily. */ public final class ConnectionPool { private static final int MAX_CONNECTIONS_TO_CLEANUP = 2; private static final long DEFAULT_KEEP_ALIVE_DURATION_MS = 5 * 60 * 1000; // 5 min private static final ConnectionPool systemDefault; static { String keepAlive = System.getProperty("http.keepAlive"); String keepAliveDuration = System.getProperty("http.keepAliveDuration"); String maxIdleConnections = System.getProperty("http.maxConnections"); long keepAliveDurationMs = keepAliveDuration != null ? Long.parseLong(keepAliveDuration) : DEFAULT_KEEP_ALIVE_DURATION_MS; if (keepAlive != null && !Boolean.parseBoolean(keepAlive)) { systemDefault = new ConnectionPool(0, keepAliveDurationMs); } else if (maxIdleConnections != null) { systemDefault = new ConnectionPool(Integer.parseInt(maxIdleConnections), keepAliveDurationMs); } else { systemDefault = new ConnectionPool(5, keepAliveDurationMs); } } /** The maximum number of idle connections for each address. */ private final int maxIdleConnections; private final long keepAliveDurationNs; private final LinkedList connections = new LinkedList<>(); /** We use a single background thread to cleanup expired connections. */ private final ExecutorService executorService = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue(), Util.threadFactory("OkHttp ConnectionPool", true)); private final Runnable connectionsCleanupRunnable = new Runnable() { @Override public void run() { List expiredConnections = new ArrayList<>(MAX_CONNECTIONS_TO_CLEANUP); int idleConnectionCount = 0; synchronized (ConnectionPool.this) { for (ListIterator i = connections.listIterator(connections.size()); i.hasPrevious(); ) { Connection connection = i.previous(); if (!connection.isAlive() || connection.isExpired(keepAliveDurationNs)) { i.remove(); expiredConnections.add(connection); if (expiredConnections.size() == MAX_CONNECTIONS_TO_CLEANUP) break; } else if (connection.isIdle()) { idleConnectionCount++; } } for (ListIterator i = connections.listIterator(connections.size()); i.hasPrevious() && idleConnectionCount > maxIdleConnections; ) { Connection connection = i.previous(); if (connection.isIdle()) { expiredConnections.add(connection); i.remove(); --idleConnectionCount; } } } for (Connection expiredConnection : expiredConnections) { Util.closeQuietly(expiredConnection.getSocket()); } } }; public ConnectionPool(int maxIdleConnections, long keepAliveDurationMs) { this.maxIdleConnections = maxIdleConnections; this.keepAliveDurationNs = keepAliveDurationMs * 1000 * 1000; } /** * Returns a snapshot of the connections in this pool, ordered from newest to * oldest. Waits for the cleanup callable to run if it is currently scheduled. */ List getConnections() { waitForCleanupCallableToRun(); synchronized (this) { return new ArrayList<>(connections); } } /** * Blocks until the executor service has processed all currently enqueued * jobs. */ private void waitForCleanupCallableToRun() { try { executorService.submit(new Runnable() { @Override public void run() { } }).get(); } catch (Exception e) { throw new AssertionError(); } } public static ConnectionPool getDefault() { return systemDefault; } /** Returns total number of connections in the pool. */ public synchronized int getConnectionCount() { return connections.size(); } /** Returns total number of spdy connections in the pool. */ public synchronized int getSpdyConnectionCount() { int total = 0; for (Connection connection : connections) { if (connection.isSpdy()) total++; } return total; } /** Returns total number of http connections in the pool. */ public synchronized int getHttpConnectionCount() { int total = 0; for (Connection connection : connections) { if (!connection.isSpdy()) total++; } return total; } /** Returns a recycled connection to {@code address}, or null if no such connection exists. */ public synchronized Connection get(Address address) { Connection foundConnection = null; for (ListIterator i = connections.listIterator(connections.size()); i.hasPrevious(); ) { Connection connection = i.previous(); if (!connection.getRoute().getAddress().equals(address) || !connection.isAlive() || System.nanoTime() - connection.getIdleStartTimeNs() >= keepAliveDurationNs) { continue; } i.remove(); if (!connection.isSpdy()) { try { Platform.get().tagSocket(connection.getSocket()); } catch (SocketException e) { Util.closeQuietly(connection.getSocket()); // When unable to tag, skip recycling and close Platform.get().logW("Unable to tagSocket(): " + e); continue; } } foundConnection = connection; break; } if (foundConnection != null && foundConnection.isSpdy()) { connections.addFirst(foundConnection); // Add it back after iteration. } executorService.execute(connectionsCleanupRunnable); return foundConnection; } /** * Gives {@code connection} to the pool. The pool may store the connection, * or close it, as its policy describes. * *

It is an error to use {@code connection} after calling this method. */ void recycle(Connection connection) { if (connection.isSpdy()) { return; } if (!connection.clearOwner()) { return; // This connection isn't eligible for reuse. } if (!connection.isAlive()) { Util.closeQuietly(connection.getSocket()); return; } try { Platform.get().untagSocket(connection.getSocket()); } catch (SocketException e) { // When unable to remove tagging, skip recycling and close. Platform.get().logW("Unable to untagSocket(): " + e); Util.closeQuietly(connection.getSocket()); return; } synchronized (this) { connections.addFirst(connection); connection.incrementRecycleCount(); connection.resetIdleStartTime(); } executorService.execute(connectionsCleanupRunnable); } /** * Shares the SPDY connection with the pool. Callers to this method may * continue to use {@code connection}. */ void share(Connection connection) { if (!connection.isSpdy()) throw new IllegalArgumentException(); executorService.execute(connectionsCleanupRunnable); if (connection.isAlive()) { synchronized (this) { connections.addFirst(connection); } } } /** Close and remove all connections in the pool. */ public void evictAll() { List connections; synchronized (this) { connections = new ArrayList<>(this.connections); this.connections.clear(); } for (int i = 0, size = connections.size(); i < size; i++) { Util.closeQuietly(connections.get(i).getSocket()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy