|
|
|
@ -40,6 +40,15 @@ import io.netty.util.concurrent.Future;
|
|
|
|
|
import io.netty.util.concurrent.FutureListener;
|
|
|
|
|
import io.netty.util.concurrent.Promise;
|
|
|
|
|
|
|
|
|
|
import org.redisson.pubsub.AsyncSemaphore;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Base connection pool class
|
|
|
|
|
*
|
|
|
|
|
* @author Nikita Koksharov
|
|
|
|
|
*
|
|
|
|
|
* @param <T> - connection type
|
|
|
|
|
*/
|
|
|
|
|
abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
|
|
|
|
|
private final Logger log = LoggerFactory.getLogger(getClass());
|
|
|
|
@ -74,7 +83,7 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
final int minimumIdleSize = getMinimumIdleSize(entry);
|
|
|
|
|
|
|
|
|
|
if (minimumIdleSize == 0 || (checkFreezed && entry.isFreezed())) {
|
|
|
|
|
initPromise.setSuccess(null);
|
|
|
|
|
initPromise.trySuccess(null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -97,12 +106,18 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<T> promise = createConnection(entry);
|
|
|
|
|
acquireConnection(entry, new Runnable() {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
Promise<T> promise = connectionManager.newPromise();
|
|
|
|
|
createConnection(entry, promise);
|
|
|
|
|
promise.addListener(new FutureListener<T>() {
|
|
|
|
|
@Override
|
|
|
|
|
public void operationComplete(Future<T> future) throws Exception {
|
|
|
|
|
if (future.isSuccess()) {
|
|
|
|
|
T conn = future.getNow();
|
|
|
|
|
|
|
|
|
|
releaseConnection(entry, conn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -119,7 +134,9 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
int value = initializedConnections.decrementAndGet();
|
|
|
|
|
if (value == 0) {
|
|
|
|
|
log.info("{} connections initialized for {}", minimumIdleSize, entry.getClient().getAddr());
|
|
|
|
|
initPromise.setSuccess(null);
|
|
|
|
|
if (!initPromise.trySuccess(null)) {
|
|
|
|
|
throw new IllegalStateException();
|
|
|
|
|
}
|
|
|
|
|
} else if (value > 0 && !initPromise.isDone()) {
|
|
|
|
|
if (requests.incrementAndGet() <= minimumIdleSize) {
|
|
|
|
|
createConnection(checkFreezed, requests, entry, initPromise, minimumIdleSize, initializedConnections);
|
|
|
|
@ -128,6 +145,13 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void acquireConnection(ClientConnectionsEntry entry, Runnable runnable) {
|
|
|
|
|
entry.acquireConnection(runnable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected abstract int getMinimumIdleSize(ClientConnectionsEntry entry);
|
|
|
|
|
|
|
|
|
@ -137,28 +161,36 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
|
|
|
|
|
public Future<T> get() {
|
|
|
|
|
for (int j = entries.size() - 1; j >= 0; j--) {
|
|
|
|
|
ClientConnectionsEntry entry = getEntry();
|
|
|
|
|
if (!entry.isFreezed() && tryAcquireConnection(entry)) {
|
|
|
|
|
return connectTo(entry);
|
|
|
|
|
final ClientConnectionsEntry entry = getEntry();
|
|
|
|
|
if (!entry.isFreezed()
|
|
|
|
|
&& tryAcquireConnection(entry)) {
|
|
|
|
|
final Promise<T> result = connectionManager.newPromise();
|
|
|
|
|
acquireConnection(entry, new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
connectTo(entry, result);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<InetSocketAddress> zeroConnectionsAmount = new LinkedList<InetSocketAddress>();
|
|
|
|
|
List<InetSocketAddress> failedAttempts = new LinkedList<InetSocketAddress>();
|
|
|
|
|
List<InetSocketAddress> freezed = new LinkedList<InetSocketAddress>();
|
|
|
|
|
for (ClientConnectionsEntry entry : entries) {
|
|
|
|
|
if (entry.isFreezed()) {
|
|
|
|
|
freezed.add(entry.getClient().getAddr());
|
|
|
|
|
} else {
|
|
|
|
|
zeroConnectionsAmount.add(entry.getClient().getAddr());
|
|
|
|
|
failedAttempts.add(entry.getClient().getAddr());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StringBuilder errorMsg = new StringBuilder(getClass().getSimpleName() + " exhausted! ");
|
|
|
|
|
StringBuilder errorMsg = new StringBuilder(getClass().getSimpleName() + " no available Redis entries. ");
|
|
|
|
|
if (!freezed.isEmpty()) {
|
|
|
|
|
errorMsg.append(" Disconnected hosts: " + freezed);
|
|
|
|
|
}
|
|
|
|
|
if (!zeroConnectionsAmount.isEmpty()) {
|
|
|
|
|
errorMsg.append(" Hosts with fully busy connections: " + zeroConnectionsAmount);
|
|
|
|
|
if (!failedAttempts.isEmpty()) {
|
|
|
|
|
errorMsg.append(" Hosts disconnected due to `failedAttempts` limit reached: " + failedAttempts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RedisConnectionException exception = new RedisConnectionException(errorMsg.toString());
|
|
|
|
@ -168,7 +200,9 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
public Future<T> get(ClientConnectionsEntry entry) {
|
|
|
|
|
if (((entry.getNodeType() == NodeType.MASTER && entry.getFreezeReason() == FreezeReason.SYSTEM) || !entry.isFreezed())
|
|
|
|
|
&& tryAcquireConnection(entry)) {
|
|
|
|
|
return connectTo(entry);
|
|
|
|
|
Promise<T> result = connectionManager.newPromise();
|
|
|
|
|
connectTo(entry, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RedisConnectionException exception = new RedisConnectionException(
|
|
|
|
@ -177,7 +211,7 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean tryAcquireConnection(ClientConnectionsEntry entry) {
|
|
|
|
|
return entry.getFailedAttempts() < config.getFailedAttempts() && entry.tryAcquireConnection();
|
|
|
|
|
return entry.getFailedAttempts() < config.getFailedAttempts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected T poll(ClientConnectionsEntry entry) {
|
|
|
|
@ -188,28 +222,31 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
return (Future<T>) entry.connect();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Future<T> connectTo(ClientConnectionsEntry entry) {
|
|
|
|
|
private void connectTo(ClientConnectionsEntry entry, Promise<T> promise) {
|
|
|
|
|
if (promise.isDone()) {
|
|
|
|
|
releaseConnection(entry);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
T conn = poll(entry);
|
|
|
|
|
if (conn != null) {
|
|
|
|
|
if (!conn.isActive()) {
|
|
|
|
|
return promiseFailure(entry, conn);
|
|
|
|
|
promiseFailure(entry, promise, conn);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return promiseSuccessful(entry, conn);
|
|
|
|
|
connectedSuccessful(entry, promise, conn);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return createConnection(entry);
|
|
|
|
|
createConnection(entry, promise);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Future<T> createConnection(final ClientConnectionsEntry entry) {
|
|
|
|
|
final Promise<T> promise = connectionManager.newPromise();
|
|
|
|
|
private void createConnection(final ClientConnectionsEntry entry, final Promise promise) {
|
|
|
|
|
Future<T> connFuture = connect(entry);
|
|
|
|
|
connFuture.addListener(new FutureListener<T>() {
|
|
|
|
|
@Override
|
|
|
|
|
public void operationComplete(Future<T> future) throws Exception {
|
|
|
|
|
if (!future.isSuccess()) {
|
|
|
|
|
releaseConnection(entry);
|
|
|
|
|
|
|
|
|
|
promiseFailure(entry, promise, future.cause());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -220,13 +257,12 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
promiseSuccessful(entry, promise, conn);
|
|
|
|
|
connectedSuccessful(entry, promise, conn);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return promise;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void promiseSuccessful(ClientConnectionsEntry entry, Promise<T> promise, T conn) {
|
|
|
|
|
private void connectedSuccessful(ClientConnectionsEntry entry, Promise<T> promise, T conn) {
|
|
|
|
|
entry.resetFailedAttempts();
|
|
|
|
|
if (!promise.trySuccess(conn)) {
|
|
|
|
|
releaseConnection(entry, conn);
|
|
|
|
@ -234,45 +270,31 @@ abstract class ConnectionPool<T extends RedisConnection> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Future<T> promiseSuccessful(ClientConnectionsEntry entry, T conn) {
|
|
|
|
|
entry.resetFailedAttempts();
|
|
|
|
|
return (Future<T>) conn.getAcquireFuture();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void promiseFailure(ClientConnectionsEntry entry, Promise<T> promise, Throwable cause) {
|
|
|
|
|
if (entry.incFailedAttempts() == config.getFailedAttempts()) {
|
|
|
|
|
checkForReconnect(entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
promise.tryFailure(cause);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void promiseFailure(ClientConnectionsEntry entry, Promise<T> promise, T conn) {
|
|
|
|
|
int attempts = entry.incFailedAttempts();
|
|
|
|
|
if (attempts == config.getFailedAttempts()) {
|
|
|
|
|
checkForReconnect(entry);
|
|
|
|
|
} else if (attempts < config.getFailedAttempts()) {
|
|
|
|
|
releaseConnection(entry, conn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
releaseConnection(entry);
|
|
|
|
|
|
|
|
|
|
RedisConnectionException cause = new RedisConnectionException(conn + " is not active!");
|
|
|
|
|
promise.tryFailure(cause);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Future<T> promiseFailure(ClientConnectionsEntry entry, T conn) {
|
|
|
|
|
private void promiseFailure(ClientConnectionsEntry entry, Promise<T> promise, T conn) {
|
|
|
|
|
int attempts = entry.incFailedAttempts();
|
|
|
|
|
if (attempts == config.getFailedAttempts()) {
|
|
|
|
|
conn.closeAsync();
|
|
|
|
|
checkForReconnect(entry);
|
|
|
|
|
} else if (attempts < config.getFailedAttempts()) {
|
|
|
|
|
releaseConnection(entry, conn);
|
|
|
|
|
} else {
|
|
|
|
|
conn.closeAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
releaseConnection(entry);
|
|
|
|
|
|
|
|
|
|
RedisConnectionException cause = new RedisConnectionException(conn + " is not active!");
|
|
|
|
|
return connectionManager.newFailedFuture(cause);
|
|
|
|
|
promise.tryFailure(cause);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void checkForReconnect(ClientConnectionsEntry entry) {
|
|
|
|
|