Eliminate abruptly closed sockets from the pool, Java

329 views Asked by At

Assume there is a Java implementation of Object Pool and the objects are successfully connected TCP Sockets.

I keep a "Clean-Up" thread at the Pool level (ConnectionFactory) that checks every N mins that whether the pool is being idle for some M mins (i.e: last access is before M mins, here M>N).

And if so then close all the additional sockets until only the core number of sockets is left in the pool.

Now I need to trace and eliminate the abruptly closed sockets as well. It seems essential because I may close all the working ones and simply end up with a pool with abruptly closed sockets (closed at the other end).

Without a doubt, now I should look into the Socket level rather than the connection factory level.

I've done a research on 'tracing abruptly closed sockets in Java', 'cleaning connection pools' and there is nothing in the Java socket API unless we sent some ACK or KeepAliveChecks (per each socket). Which means I need to be performing this on every socket in a routine basis.

What is the best way (i.e: is there any other way) so I could end up keeping the good guys (well connected sockets) in my pool?

How to clean-up the abruptly closed sockets in my pool?

2

There are 2 answers

3
kino lucky On

Interesting ,I just wrote a socket connection pool yesterday.

  • there is not a daemon thread to check.
  • It will check passive as trying to get a connection
    • connection.isClosed will check the socket status, of course it is not reliable
    • to check if the connection expired
  • And it will check as returning a connection. you must mark a connection closed while there is an exception using it.

/** * @author:xingchaowang * @date: 8/14/2014. */

public class ConnectionPoolImpl implements ConnectionPool {

private volatile int minConnections;
private volatile int maxConnections;
private volatile long connectionTTL;
private volatile long leaseTimeout = 1000;

private AtomicInteger pending = new AtomicInteger(0);

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

private LinkedList<Connection> free = new LinkedList<Connection>();
private Set<Connection> leased = new HashSet<Connection>();

private ConnectionFactory connectionFactory;

public ConnectionPoolImpl(int minConnections, int maxConnections, long connectionTTL, long leaseTimeout, ConnectionFactory connectionFactory) {

    this.minConnections = minConnections;
    this.maxConnections = maxConnections;
    this.connectionTTL = connectionTTL;
    this.leaseTimeout = leaseTimeout;
    this.connectionFactory = connectionFactory;
}

@Override
public Connection lease() throws Exception {
    return lease(1000);
}

@Override
public Connection lease(long timeout) throws Exception {
    pending.incrementAndGet();
    lock.lock();
    try {
        Connection connection = null;
        Date deadLine = new Date(System.currentTimeMillis() + timeout);

        while (true) {
            long now = System.currentTimeMillis();

            //If the count of existing connections is less than minConnections, create new one and return.
            if (_connectionCount() < minConnections) {
                return _createAndLease();
            }

            //Try to get a connection from the free list.
            while ((connection = free.pollFirst()) != null) {
                if (connection.isClosed()) {
                    continue;
                } else if (connection.getCreatedTime() + connectionTTL < now) {
                    connection.close();
                }else{
                    leased.add(connection);
                    return connection;
                }
            }

            //Free list is empty, try to create new one if doesn't reach the upper limit maxConnections.
            if (_connectionCount() < maxConnections) {
                return _createAndLease();
            }

            condition.awaitUntil(deadLine);

            //Try to get again if doesn't reach the deadLine, or return by throwing a TimeoutException.
            if (deadLine.getTime() >= System.currentTimeMillis()) {
                throw new TimeoutException("Timeout waiting for connection");
            }
        }
    } finally {
        lock.unlock();
        pending.decrementAndGet();
    }
}

@Override
public void release(Connection connection) {
    lock.lock();
    try{
        long now = System.currentTimeMillis();

        leased.remove(connection);

        if (connection.isClosed()) {
            return;
        } else if (connection.getCreatedTime() + connectionTTL < now) {
            connection.close();
        }else{
            free.add(connection);
        }
    }finally {
        condition.signal();
        lock.unlock();
    }
}

@Override
public PoolStats poolStats() {
    return new PoolStats(leased.size(),free.size(),pending.get(),minConnections,maxConnections);
}

private int _connectionCount() {
    return free.size() + leased.size();
}

private Connection _createAndLease() throws Exception {
    Connection connection;
    connection = connectionFactory.create();
    leased.add(connection);
    return connection;
}

public int getMinConnections() {
    return minConnections;
}

public void setMinConnections(int minConnections) {
    this.minConnections = minConnections;
}

public int getMaxConnections() {
    return maxConnections;
}

public void setMaxConnections(int maxConnections) {
    this.maxConnections = maxConnections;
}

public long getConnectionTTL() {
    return connectionTTL;
}

public void setConnectionTTL(long connectionTTL) {
    this.connectionTTL = connectionTTL;
}

public long getLeaseTimeout() {
    return leaseTimeout;
}

public void setLeaseTimeout(long leaseTimeout) {
    this.leaseTimeout = leaseTimeout;
}

}

1
zigot On

I'm not aware of any other ways to check if a socket is alive. You can use a TimerTask to send routine KeepAliveChecks and remove the sockets which do not pass the check.