Redis2.1.0:Could not get a resource from the poolの分析
節前の最終日、ssoログインに問題が発生し、多くのユーザーが正常にログインできなかった.クエリー・ログでは、次のようにエラーが発生しました.
エラーログには、次のような2015-02-15 08:40:30536 INFO[org.jasig.cas.authentication.AuthenticationManagerImpl]-.gaochao.oa.module.sso.authentication.handler.FxLdapMixAuthenticationHandler successfully authenticated[username: wei.guo]>2015-02-15 08:40:30,536 INFO [org.jasig.cas.authentication.AuthenticationManagerImpl] - 2015-02-15 08:40:30,536 INFO [org.jasig.cas.authentication.AuthenticationManagerImpl] - .gaochao.oa.module.sso.authentication.handler.FxLdapMixAuthenticationHandler@7952dda7 authenticated wei.guo with credential [username: wei.guo].>2015-02-15 08:40:30,537 INFO [com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - 2015-02-15 08:40:30,576 ERROR [com.gaochao.oa.module.sso.ticket.registry.RedisTicketRegistry] - :Could not get a resource from the pool>2015-02-15 08:40:30,576 INFO [com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - 2015-02-15 08:40:30,955 ERROR [com.gaochao.oa.module.sso.ticket.registry.RedisTicketRegistry] -
以上のログから、問題コードがssoにあることがわかります.赤い太字の部分から、ssoサーバ証明書とユーザー認証が成功したことがわかります.
ただし、認証サービス証明書ticketを生成し、リソースを取得できませんでした.
このコードにはRedis異常やその他の異常が報告される可能性があり,Could not get a resource from the poolによりredis異常と考えられる.さらに異常が報告された場所を追跡します.次のコードを参照してください.
インターネットで資料を検索すると、この異常はredisのプール接続構成と関係があると考えられています.
赤い部分が発生する可能性のある原因の1つは、クライアントがredisサーバに接続を取りに行った場合(コードは、レンタルオブジェクトborrowObjectを記述している)、プールに使用可能な接続がないこと、すなわち、プール内のすべての接続が占有され、待機時に設定されたタイムアウト時間後に取得されていない場合に報告される.
解決策:
1.JedisPoolConfigのmaxActiveを自分のシステムに適合するバルブ値に調整します.もちろん、この場合、サービスを再起動し、プール接続を再構築することは必然的に可能です.
私たちのシステムのredis構成を確認したとき、redis.propertiesではredisとして構成する.maxIdle=5000,
理論的には5000の同時量は十分で、資源が手に入らないことはありませんが、私たちのシステムが現れました.資料を調べ続けてみると、この問題の原因は私たちが使っているredis 2かもしれません.1.0(redisは2.4.0以降手動で返す必要はありません)手動でredisを返す必要がありますが、私たちの呼び出しは返されず、接続が長期的に占有されています.例えば私たちが自分で書き直したクラスcomです.gaochao.oa.module.sso.ticket.registry.RedisTicketRegistry.JAvaのgetTicketsはリソースを取得するだけで、返す場所はありません.ここで頻繁に呼び出されると,5000の場合,すなわちリソース接続が不十分な場合が長くない可能性が高い.次のようになります.
以上の問題があれば、提案します.
1.redisをredis 2.4.0以上のバージョンに置き換える.
2.自分で書いたクラスを読み取り、redisリソースを取得する場所は解放する必要があります(一般的にredisを使用する場所にはfinally、すなわち最終的に実行しなければならないコードがあり、接続を返す責任があります).
次のようになります.
また,その他の原因はネットワーク異常,redisサーバ異常などである可能性がある.
節前の初歩的な解決策は、2日ごとにsso、redisサーバを再起動し、節後にコードを読み取り、リソースを取得した後に解放されていないコードをすべて修復することです.
エラーログには、次のような2015-02-15 08:40:30536 INFO[org.jasig.cas.authentication.AuthenticationManagerImpl]-.gaochao.oa.module.sso.authentication.handler.FxLdapMixAuthenticationHandler successfully authenticated[username: wei.guo]>2015-02-15 08:40:30,536 INFO [org.jasig.cas.authentication.AuthenticationManagerImpl] - 2015-02-15 08:40:30,536 INFO [org.jasig.cas.authentication.AuthenticationManagerImpl] - .gaochao.oa.module.sso.authentication.handler.FxLdapMixAuthenticationHandler@7952dda7 authenticated wei.guo with credential [username: wei.guo].>2015-02-15 08:40:30,537 INFO [com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - 2015-02-15 08:40:30,576 ERROR [com.gaochao.oa.module.sso.ticket.registry.RedisTicketRegistry] - :Could not get a resource from the pool>2015-02-15 08:40:30,576 INFO [com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - 2015-02-15 08:40:30,955 ERROR [com.gaochao.oa.module.sso.ticket.registry.RedisTicketRegistry] -
以上のログから、問題コードがssoにあることがわかります.赤い太字の部分から、ssoサーバ証明書とユーザー認証が成功したことがわかります.
if (!authenticationHandler.authenticate(credentials)) {
log.info("{} failed to authenticate {}", handlerName, credentials);
} else {
log.info("{} successfully authenticated {}", handlerName,
credentials);
authenticatedClass = authenticationHandler;
authenticated = true;
break;
}
ただし、認証サービス証明書ticketを生成し、リソースを取得できませんでした.
try {
jedis = getResource();
byte[] b = jedis.hget(SSO_HASH_ID, ticketId.getBytes());
if (b == null || b.length == 0) {
return null;
}
ByteArrayInputStream bais = new ByteArrayInputStream(b);
ois = new ObjectInputStream(bais);
ticket = (Ticket) ois.readObject();
} catch (JedisException e) {
borrowOrOprSuccess = false;
returnBrokenResource(jedis);
log.error("Failed getTicket {}, error message :{}", ticketId,
e.getMessage());
} catch (Exception e) {
log.error("Failed getTicket {}, error message :{}", ticketId,
e.getMessage());
}
このコードにはRedis異常やその他の異常が報告される可能性があり,Could not get a resource from the poolによりredis異常と考えられる.さらに異常が報告された場所を追跡します.次のコードを参照してください.
@SuppressWarnings("unchecked")
public T getResource() {
try {
return (T) internalPool.borrowObject();
} catch (Exception e) {
throw new JedisConnectionException(
"Could not get a resource from the pool", e);
}
}
インターネットで資料を検索すると、この異常はredisのプール接続構成と関係があると考えられています.
赤い部分が発生する可能性のある原因の1つは、クライアントがredisサーバに接続を取りに行った場合(コードは、レンタルオブジェクトborrowObjectを記述している)、プールに使用可能な接続がないこと、すなわち、プール内のすべての接続が占有され、待機時に設定されたタイムアウト時間後に取得されていない場合に報告される.
解決策:
1.JedisPoolConfigのmaxActiveを自分のシステムに適合するバルブ値に調整します.もちろん、この場合、サービスを再起動し、プール接続を再構築することは必然的に可能です.
私たちのシステムのredis構成を確認したとき、redis.propertiesではredisとして構成する.maxIdle=5000,
# Redis settings
redis.maxIdle=5000
redis.maxActive=5000
redis.maxWait=10000
redis.testOnBorrow=true
理論的には5000の同時量は十分で、資源が手に入らないことはありませんが、私たちのシステムが現れました.資料を調べ続けてみると、この問題の原因は私たちが使っているredis 2かもしれません.1.0(redisは2.4.0以降手動で返す必要はありません)手動でredisを返す必要がありますが、私たちの呼び出しは返されず、接続が長期的に占有されています.例えば私たちが自分で書き直したクラスcomです.gaochao.oa.module.sso.ticket.registry.RedisTicketRegistry.JAvaのgetTicketsはリソースを取得するだけで、返す場所はありません.ここで頻繁に呼び出されると,5000の場合,すなわちリソース接続が不十分な場合が長くない可能性が高い.次のようになります.
/**
* @author **
* @date 2013-7-15 10:13:19
* @see org.jasig.cas.ticket.registry.TicketRegistry#getTickets()
* @return
*/
public Collection getTickets() {
Jedis jedis = null;
jedis = getResource();
Map map = jedis.hgetAll(SSO_HASH_ID);
if(CollectionUtils.isEmpty(map)) {
return new ArrayList();
}
Map result = new HashMap(map.size());
for(Iterator> iterator = map.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = iterator.next();
// Object jsonValue = redisSerializer.parseObject(entry.getValue());
String ticketId = new String(entry.getKey());
Ticket ticket = getTicket(ticketId);
if(ticket != null) {
result.put(ticketId, ticket);
}
}
return result.values();
}
以上の問題があれば、提案します.
1.redisをredis 2.4.0以上のバージョンに置き換える.
2.自分で書いたクラスを読み取り、redisリソースを取得する場所は解放する必要があります(一般的にredisを使用する場所にはfinally、すなわち最終的に実行しなければならないコードがあり、接続を返す責任があります).
次のようになります.
finally {
IOUtils.close(ois);
if (borrowOrOprSuccess) {
//
returnResource(jedis);
}
}
また,その他の原因はネットワーク異常,redisサーバ異常などである可能性がある.
節前の初歩的な解決策は、2日ごとにsso、redisサーバを再起動し、節後にコードを読み取り、リソースを取得した後に解放されていないコードをすべて修復することです.