天天看点

Redis2.1.0:Could not get a resource from the pool的分析

节前的最后一天,sso登陆出现问题,多数用户无法正常登陆。经查询日志,报错如下:

错误日志截取如下

2015-02-15 08:40:30,536 INFO [org.jasig.cas.authentication.AuthenticationManagerImpl] - <com.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] - <Resolved principal wei.guo>

2015-02-15 08:40:30,536 INFO [org.jasig.cas.authentication.AuthenticationManagerImpl] - <com.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] - <Audit trail record BEGIN

2015-02-15 08:40:30,576 ERROR [com.gaochao.oa.module.sso.ticket.registry.RedisTicketRegistry] - <Failed adding TGT-18015-B0igJB5vgG03iiVqXKl5VSO0tnBtYipfauzcKLCfD2MvhVYTEP-cas01.example.org, error message :Could not get a resource from the pool>

2015-02-15 08:40:30,576 INFO [com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN

2015-02-15 08:40:30,955 ERROR [com.gaochao.oa.module.sso.ticket.registry.RedisTicketRegistry] - <Failed adding TGT-18016-nMaZefSqRG393dcANeR4S1Tc2ff1WWdCafrNGwESgXkwLkCfN3-cas01.example.org, error message :Could not get a resource from the pool>

  通过以上日志可以看出问题代码在sso中,红色加粗部分可以看出sso服务器证书以及用户鉴权是成功的。

1

2

3

4

5

6

7

8

9

<code>if</code> <code>(!authenticationHandler.authenticate(credentials)) {</code>

<code>            </code><code>log.info(</code><code>"{} failed to authenticate {}"</code><code>, handlerName, credentials);</code>

<code>        </code><code>} </code><code>else</code> <code>{</code>

<code>            </code><code>log.info(</code><code>"{} successfully authenticated {}"</code><code>, handlerName,</code>

<code>                    </code><code>credentials);</code>

<code>            </code><code>authenticatedClass = authenticationHandler;</code>

<code>            </code><code>authenticated = </code><code>true</code><code>;</code>

<code>            </code><code>break</code><code>;</code>

<code>        </code><code>}</code>

  但是生成认证服务凭据ticket并且获得资源时失败

10

11

12

13

14

15

16

17

18

<code>try</code> <code>{</code>

<code>            </code><code>jedis = getResource();</code>

<code>            </code><code>byte</code><code>[] b = jedis.hget(SSO_HASH_ID, ticketId.getBytes());</code>

<code>            </code><code>if</code> <code>(b == </code><code>null</code> <code>|| b.length == </code><code>0</code><code>) {</code>

<code>                </code><code>return</code> <code>null</code><code>;</code>

<code>            </code><code>}</code>

<code>            </code><code>ByteArrayInputStream bais = </code><code>new</code> <code>ByteArrayInputStream(b);</code>

<code>            </code><code>ois = </code><code>new</code> <code>ObjectInputStream(bais);</code>

<code>            </code><code>ticket = (Ticket) ois.readObject();</code>

<code>        </code><code>} </code><code>catch</code> <code>(JedisException e) {</code>

<code>            </code><code>borrowOrOprSuccess = </code><code>false</code><code>;</code>

<code>            </code><code>returnBrokenResource(jedis);</code>

<code>            </code><code>log.error(</code><code>"Failed getTicket {}, error message :{}"</code><code>, ticketId,</code>

<code>                    </code><code>e.getMessage());</code>

<code>        </code><code>} </code><code>catch</code> <code>(Exception e) {</code>

    这段代码中可能报出Redis异常和其他异常,通过Could not get a resource from the pool,可以认为是redis异常。再进一步跟踪报出异常的地方,见如下代码:

<code>@SuppressWarnings</code><code>(</code><code>"unchecked"</code><code>) </code>

<code>        </code><code>public</code> <code>T getResource() { </code>

<code>        </code><code>try</code> <code>{ </code>

<code>        </code><code>return</code> <code>(T) internalPool.borrowObject(); </code>

<code>        </code><code>} </code><code>catch</code> <code>(Exception e) { </code>

<code>        </code><code>throw</code> <code>new</code> <code>JedisConnectionException( </code>

<code>        </code><code>"Could not get a resource from the pool"</code><code>, e); </code>

<code>        </code><code>} </code>

解决办法:

1.调整JedisPoolConfig中maxActive为适合自己系统的阀值,当然这种情况下重启服务并将重建池连接是必然可行的。

我在查看我们系统的redis配置时,redis.properties中配置为redis.maxIdle=5000,

<code># Redis settings</code>

<code>redis.maxIdle=5000</code>

<code>redis.maxActive=5000</code>

<code>redis.maxWait=10000</code>

<code>redis.testOnBorrow=true</code>

19

20

21

22

23

24

25

<code>    </code><code>/** </code>

<code>     </code><code>* @author **</code>

<code>     </code><code>* @date 2013-7-15 下午10:13:19</code>

<code>     </code><code>* @see org.jasig.cas.ticket.registry.TicketRegistry#getTickets()</code>

<code>     </code><code>* @return</code>

<code>     </code><code>*/</code>

<code>    </code><code>public</code> <code>Collection&lt;Ticket&gt; getTickets() {</code>

<code>        </code><code>Jedis jedis = </code><code>null</code><code>;</code>

<code>        </code><code>jedis = getResource();</code>

<code>        </code><code>Map&lt;</code><code>byte</code><code>[], </code><code>byte</code><code>[]&gt; map = jedis.hgetAll(SSO_HASH_ID);</code>

<code>        </code><code>if</code><code>(CollectionUtils.isEmpty(map)) {</code>

<code>            </code><code>return</code> <code>new</code> <code>ArrayList&lt;Ticket&gt;();</code>

<code>        </code><code>Map&lt;String, Ticket&gt; result = </code><code>new</code> <code>HashMap(map.size());</code>

<code>        </code><code>for</code><code>(Iterator&lt;Map.Entry&lt;</code><code>byte</code><code>[], </code><code>byte</code><code>[]&gt;&gt; iterator = map.entrySet().iterator(); iterator.hasNext();) {</code>

<code>            </code><code>Map.Entry&lt;</code><code>byte</code><code>[], </code><code>byte</code><code>[]&gt; entry = iterator.next();</code>

<code>//          Object jsonValue = redisSerializer.parseObject(entry.getValue());</code>

<code>            </code><code>String ticketId = </code><code>new</code> <code>String(entry.getKey());</code>

<code>            </code><code>Ticket ticket = getTicket(ticketId);</code>

<code>            </code><code>if</code><code>(ticket != </code><code>null</code><code>) {</code>

<code>                </code><code>result.put(ticketId, ticket);</code>

<code>        </code><code>return</code> <code>result.values();</code>

<code>    </code><code>}</code>

  如是以上问题,建议

1.redis换用redis 2.4.0以上版本;

2.对我们自己写的类进行走读,凡获取redis资源的地方必须需释放(一般使用redis的地方都有一个finally即最终必须执行的代码负责归还连接)

如下:

<code>finally</code> <code>{ </code>

<code>        </code><code>IOUtils.close(ois); </code>

<code>        </code><code>if</code> <code>(borrowOrOprSuccess) { </code>

<code>        </code><code>//返回到资源池 </code>

<code>        </code><code>returnResource(jedis); </code>

  另外,其他的原因还可能是网络异常、redis服务器异常等。

  节前初步解决方案是每两天重启sso、redis服务器,另外节后对代码进行走读,将获取资源后未释放的代码全部进行修复。

     本文转自 gaochaojs 51CTO博客,原文链接:http://blog.51cto.com/jncumter/1615620,如需转载请自行联系原作者