節前的最後一天,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<Ticket> getTickets() {</code>
<code> </code><code>Jedis jedis = </code><code>null</code><code>;</code>
<code> </code><code>jedis = getResource();</code>
<code> </code><code>Map<</code><code>byte</code><code>[], </code><code>byte</code><code>[]> 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<Ticket>();</code>
<code> </code><code>Map<String, Ticket> result = </code><code>new</code> <code>HashMap(map.size());</code>
<code> </code><code>for</code><code>(Iterator<Map.Entry<</code><code>byte</code><code>[], </code><code>byte</code><code>[]>> iterator = map.entrySet().iterator(); iterator.hasNext();) {</code>
<code> </code><code>Map.Entry<</code><code>byte</code><code>[], </code><code>byte</code><code>[]> 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,如需轉載請自行聯系原作者