ServiceStack.Redis的PooledRedisClientManager蛋痛的設計


PooledRedisClientManager是ServiceStack.Redis的連接池管理類,通過連接池可以實現更高效的Redis操作.但PooledRedisClientManager相關GetClient的設計似乎存在一些問題,如果你只Pool只指向一台Redis這倒不會有什么問題,但如果指向多台Redis那就可能產生悲劇的事情.下面解釋一下指向多台Redis存在的一些問題.

具體代碼

 1 /// <summary>
 2         /// Called within a lock
 3         /// </summary>
 4         /// <returns></returns>
 5         private RedisClient GetInActiveWriteClient()
 6         {
 7             var desiredIndex = WritePoolIndex % writeClients.Length;
 8             //this will loop through all hosts in readClients once even though there are 2 for loops
 9             //both loops are used to try to get the prefered host according to the round robin algorithm
10             for (int x = 0; x < ReadWriteHosts.Count; x++)
11             {
12                 var nextHostIndex = (desiredIndex + x) % ReadWriteHosts.Count;
13                 var nextHost = ReadWriteHosts[nextHostIndex];
14                 for (var i = nextHostIndex; i < writeClients.Length; i += ReadWriteHosts.Count)
15                 {                    
16                     if (writeClients[i] != null && !writeClients[i].Active && !writeClients[i].HadExceptions)
17                         return writeClients[i];
18                     else if (writeClients[i] == null || writeClients[i].HadExceptions)
19                     {
20                         if (writeClients[i] != null)
21                             writeClients[i].DisposeConnection();
22                         var client = RedisClientFactory.CreateRedisClient(nextHost.Host, nextHost.Port);
23 
24                         if (nextHost.RequiresAuth)
25                             client.Password = nextHost.Password;
26 
27                         client.Id = RedisClientCounter++;
28                         client.ClientManager = this;
29                         client.NamespacePrefix = NamespacePrefix;
30                         client.ConnectionFilter = ConnectionFilter;
31                         
32                         writeClients[i] = client;
33 
34                         return client;
35                     }
36                 }
37             }
38             return null;
39         }

工作原理

以上代碼的原理非常簡單,就是輪循不同host下的可用連接,如果相關連接可用測直接返回.如果連接損耗則釋放重新創建.

存在問題

如果只使用一個host倒沒什么問題,但使用多個host的時候那你會發現如果其中一台的redis服務異常那對應的host還是會被輪循到,那就會導致輪循到應該服務的操作所有都異常,還有更悲劇的情況就是當一台服務器擋機了,就會導致連接到對應host的連接創建超時導致程序長時間等待然后報錯,這情況對於並發應來說算是一個非常悲劇的事情.

解決方法

其實可以針對host來划分節點,每個節點存自有的連接池.當相關host的連接操作出現網絡異常的時候,應該把host從當前輪循機制中排除.這樣可以快速地保證操作會馬上遷移到正常的host上面去.

建立一個host恢復機制,PooledRedisClientManager應該存在一個內部機制對損壞的host 進行檢測,通過connect到redis執行ping指令來確認host是否正常,如果正常馬上把host恢復到輪循范圍內.

思考

作者在連接的存儲上並沒有使用Stack,其實使用Stack在設計和管理上更簡單,也是許是為不想在連接push到池中存在線程同步處理環節的開銷.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM