com.wandoulabs.nedis.NedisClientPoolImpl Maven / Gradle / Ivy
/**
* Copyright (c) 2015 Wandoujia Inc.
*
* Licensed 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.wandoulabs.nedis;
import static com.wandoulabs.nedis.util.NedisUtils.getEventExecutor;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import java.net.SocketAddress;
import java.util.ArrayList;
import com.wandoulabs.nedis.util.NedisClientHashSet;
/**
* @author Apache9
*/
public class NedisClientPoolImpl implements NedisClientPool {
private final EventLoopGroup group;
private final Class extends Channel> channelClass;
private final long timeoutMs;
private final SocketAddress remoteAddress;
private final byte[] password;
private final int database;
private final byte[] clientName;
private final int maxPooledConns;
private final boolean exclusive;
private final NedisClientHashSet pool;
private final Promise closePromise;
private int numConns;
private boolean closed = false;
public NedisClientPoolImpl(EventLoopGroup group, Class extends Channel> channelClass,
long timeoutMs, SocketAddress remoteAddress, byte[] password, int database,
byte[] clientName, int maxPooledConns, boolean exclusive) {
this.group = group;
this.channelClass = channelClass;
this.timeoutMs = timeoutMs;
this.remoteAddress = remoteAddress;
this.password = password;
this.database = database;
this.clientName = clientName;
this.maxPooledConns = maxPooledConns;
this.exclusive = exclusive;
this.pool = new NedisClientHashSet(maxPooledConns);
this.closePromise = group.next().newPromise();
}
private final class InitializeFutureListener implements FutureListener {
private final Promise promise;
private final NedisClientImpl client;
private final State nextState;
public InitializeFutureListener(Promise promise, NedisClientImpl client,
State nextState) {
this.promise = promise;
this.client = client;
this.nextState = nextState;
}
@Override
public void operationComplete(Future future) throws Exception {
if (future.isSuccess()) {
initialize(promise, client, nextState);
} else {
promise.tryFailure(future.cause());
client.close();
}
}
}
private enum State {
AUTH, SELECT, CLIENT_SETNAME, FINISH
}
private void initialize(final Promise promise, final NedisClientImpl client,
State state) {
switch (state) {
case AUTH:
if (password == null) {
initialize(promise, client, State.SELECT);
} else {
client.auth0(password).addListener(
new InitializeFutureListener(promise, client, State.SELECT));
}
break;
case SELECT:
if (database == 0) {
initialize(promise, client, State.CLIENT_SETNAME);
} else {
client.select0(database).addListener(
new InitializeFutureListener(promise, client, State.CLIENT_SETNAME));
}
break;
case CLIENT_SETNAME:
if (clientName == null) {
promise.trySuccess(client);
} else {
client.clientSetname0(clientName).addListener(
new InitializeFutureListener(promise, client, State.FINISH));
}
break;
case FINISH:
promise.trySuccess(client);
break;
}
}
private Future newClient() {
Future f = NedisClientBuilder.create().group(group).channel(channelClass)
.timeoutMs(timeoutMs).belongTo(this).connect(remoteAddress);
final Promise promise = getEventExecutor(f).newPromise();
f.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (future.isSuccess()) {
initialize(promise, future.getNow(), State.AUTH);
} else {
promise.tryFailure(future.cause());
}
}
});
return promise;
}
private final FutureListener acquireFutureListener = new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
synchronized (pool) {
if (future.isSuccess()) {
final NedisClient client = future.getNow();
client.closeFuture().addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
synchronized (pool) {
pool.remove(client);
numConns--;
if (closed && numConns == 0) {
closePromise.trySuccess(null);
}
}
}
});
if (!exclusive) {
if (closed) {
client.close();
} else {
pool.add(client);
if (!pendingAcquireList.isEmpty()) {
for (Promise promise: pendingAcquireList) {
promise.trySuccess(client);
}
pendingAcquireList.clear();
// usually we do not need this any more, so trim its size.
pendingAcquireList.trimToSize();
}
}
}
} else {
numConns--;
if (!exclusive && numConns == 0) {
// notify all pending promises that we could not get a connection.
for (Promise promise: pendingAcquireList) {
promise.tryFailure(future.cause());
}
pendingAcquireList.clear();
}
}
}
}
};
private final ArrayList> pendingAcquireList = new ArrayList<>();
@Override
public Future acquire() {
synchronized (pool) {
if (closed) {
return group.next().newFailedFuture(
new IllegalStateException("already closed"));
}
if (numConns < maxPooledConns) {
numConns++;
return newClient().addListener(acquireFutureListener);
}
if (!pool.isEmpty()) {
NedisClient client = pool.head(exclusive);
return client.eventLoop().newSucceededFuture(client);
}
if (exclusive) {
numConns++;
return newClient().addListener(acquireFutureListener);
} else {
// If connection is shared, then we should not create more connections than
// maxPooledConns. So here we add a promise to pending queue. The promise will be
// notified when there are connections in pool.
Promise promise = group.next().newPromise();
pendingAcquireList.add(promise);
return promise;
}
}
}
private void tryPooling(NedisClient client) {
if (closed) {
client.close();
return;
}
if (pool.contains(client)) {
return;
}
if (pool.size() < maxPooledConns) {
pool.add(client);
} else {
client.close();
}
}
@Override
public void release(NedisClient client) {
synchronized (pool) {
if (client.isOpen()) {
tryPooling(client);
}
}
}
@Override
public Future close() {
NedisClient[] toClose;
synchronized (pool) {
if (closed) {
return closePromise;
}
closed = true;
toClose = pool.toArray();
}
for (NedisClient client: toClose) {
client.close();
}
return closePromise;
}
@Override
public Future closeFuture() {
return closePromise;
}
@Override
public boolean exclusive() {
return exclusive;
}
@Override
public int numConns() {
synchronized (pool) {
return numConns;
}
}
@Override
public int numPooledConns() {
synchronized (pool) {
return pool.size();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy