1.設置testOnBorrow爲true
該法是在借用jedis時進行檢測,如下源碼
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
assertOpen();
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
(getNumIdle() < 2) &&
(getNumActive() > getMaxTotal() - 3) ) {
removeAbandoned(ac);
}
PooledObject<T> p = null;
// Get local copy of current config so it is consistent for entire
// method execution
final boolean blockWhenExhausted = getBlockWhenExhausted();
boolean create;
final long waitTime = System.currentTimeMillis();
while (p == null) {
create = false;
p = idleObjects.pollFirst();
// 初始,第一次獲取jedis連接
if (p == null) {
p = create();
if (p != null) {
create = true;
}
}
// 已經沒有可用的jedis
if (blockWhenExhausted) {
if (p == null) {
if (borrowMaxWaitMillis < 0) {
p = idleObjects.takeFirst();
} else {
p = idleObjects.pollFirst(borrowMaxWaitMillis,
TimeUnit.MILLISECONDS);
}
}
if (p == null) {
throw new NoSuchElementException(
"Timeout waiting for idle object");
}
} else {
if (p == null) {
throw new NoSuchElementException("Pool exhausted");
}
}
if (!p.allocate()) {
p = null;
}
if (p != null) {
try {
// 激活jedis,就是更新jedis狀態
factory.activateObject(p);
} catch (final Exception e) {
try {
destroy(p);
} catch (final Exception e1) {
// Ignore - activation failure is more important
}
p = null;
if (create) {
final NoSuchElementException nsee = new NoSuchElementException(
"Unable to activate object");
nsee.initCause(e);
throw nsee;
}
}
// 進行檢測
if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
boolean validate = false;
Throwable validationThrowable = null;
try {
validate = factory.validateObject(p);
} catch (final Throwable t) {
PoolUtils.checkRethrow(t);
validationThrowable = t;
}
if (!validate) {
try {
// 不符合要求,便銷燬該jedis,新建一個jedis於池中,下一輪循環獲取jedis連接
destroy(p);
destroyedByBorrowValidationCount.incrementAndGet();
} catch (final Exception e) {
// Ignore - validation failure is more important
}
p = null;
if (create) {
final NoSuchElementException nsee = new NoSuchElementException(
"Unable to validate object");
nsee.initCause(validationThrowable);
throw nsee;
}
}
}
}
}
updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
return p.getObject();
}
2.設置testOnReturn爲true
該法是在歸還jedis時進行檢測,如下源碼
public void returnObject(final T obj) {
final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
if (p == null) {
if (!isAbandonedConfig()) {
throw new IllegalStateException(
"Returned object not currently part of this pool");
}
return; // Object was abandoned and removed
}
markReturningState(p);
final long activeTime = p.getActiveTimeMillis();
// 歸還時檢測jedis
if (getTestOnReturn() && !factory.validateObject(p)) {
try {
// 銷燬該jedis,新建一個jedis於池中,下一輪循環獲取jedis連接
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
try {
ensureIdle(1, false);
} catch (final Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
}
.......
updateStatsReturn(activeTime);
}
3.returnBrokenResource
該法最終使用invalidateObject關閉jedis
public void invalidateObject(final T obj) throws Exception {
final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
if (p == null) {
if (isAbandonedConfig()) {
return;
}
throw new IllegalStateException(
"Invalidated object not currently part of this pool");
}
synchronized (p) {
if (p.getState() != PooledObjectState.INVALID) {
// 銷燬該jedis,新建一個jedis於池中,下一輪循環獲取jedis連接
destroy(p);
}
}
ensureIdle(1, false);
}
4.聯合使用quit,disconnect,close方法
前兩種方法分別是在借和歸還jedis於jedisPool時進行檢測,如果發現異常,便會直接銷燬該jedis,並且新建一個jedis使用。
第三種方法,已經被jedis作爲廢棄接口。
第四種方法,雖然沒有像前三種方法一樣,直接銷燬掉該jedis實例,但是爲啥能達到效果呢?
-
quit是請求服務端關閉與該客戶端的連接。
-
disconnect是客戶端關閉客戶端與服務端的連接。
public void disconnect() {
if (isConnected()) {
try {
outputStream.flush();
socket.close();
} catch (IOException ex) {
broken = true;
throw new JedisConnectionException(ex);
} finally {
IOUtils.closeQuietly(socket);
}
}
}
-
close便是歸還該jedis。
如果沒有使用disconnect,相當於只關閉了服務端與客戶端這一方向的連接,衆所周知,socket連接是雙端的,所以單向關閉連接後,再次客戶端發起請求,會報異常。
Exception in thread “main”
redis.clients.jedis.exceptions.JedisConnectionException:
java.net.SocketException: Connection reset at
redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:202)
at
redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40)
at redis.clients.jedis.Protocol.process(Protocol.java:151) at
redis.clients.jedis.Protocol.read(Protocol.java:215) at
redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
at
redis.clients.jedis.Connection.getBinaryMultiBulkReply(Connection.java:276)
at
redis.clients.jedis.Connection.getMultiBulkReply(Connection.java:269)
at redis.clients.jedis.Jedis.mget(Jedis.java:407)
只有使用了disconnect才能達到雙端都關閉連接,避免上述異常。
以上便是4種銷燬或重置異常jedis的方式了。