標籤:des style blog http color 使用
普通的zk用法,如下寫法:
zk.Exists("/aaa", true);zk.Create(...);
但是由於這些API會拋Zookeeper的Exception,比如ConnectionLossException, NoNodeException等,所以必須配合一堆try/catch的機制來catch錯誤,catch後再處理...
寫起來很麻煩
因此寫了個RetryHelper來封裝上面這個try/catch行為,用起來也比較方便,如下:
RetryHelper helper=RetryHelper.Make();helper.CreateNodeStructure = () => { Console.WriteLine("CreateNodeStructure"); };helper.FixConnectionLossAction = () => { Console.WriteLine("FixConnectionLossAction");};helper.IfErrorThen = () => { Console.WriteLine("IfErrorThen"); };helper.Execute(() =>{ this.zk.GetChildren(...);});
上面的意思是如果在Execute中,如果報錯了,則會看報錯的是哪種類型,如果是ConnectionLoss則執行FixConnectionLossAction委託,如果是NoNode則執行建立節點的委託
也就是將最常見的2個zookeeper動作給結構化了:建立節點目錄結構以及串連丟失時的重新串連動作
RetryHelper代碼:
public class RetryHelper { private int retryDelay = 500; private long signal = 0; public Action IfErrorThen; public Action CreateNodeStructure; public Action FixConnectionLossAction; public static RetryHelper Make() { return new RetryHelper(); } public void Execute(Action action) { while (true) { try { action(); break; } catch (ZooKeeperNet.KeeperException.NoNodeException ex) { //create node structure Console.WriteLine("retry helper NoNodeException: " + ex.Message); if (CreateNodeStructure != null) RetryHelper.Make().Execute(CreateNodeStructure); continue; } catch (ZooKeeperNet.KeeperException.ConnectionLossException ex) { Console.WriteLine("retry helper ConnectionLossException: " + ex.Message); long attempSignal = Interlocked.Read(ref signal); while (Interlocked.Read(ref signal) > 0) Thread.Sleep(retryDelay); if (attempSignal == 0) { Interlocked.Increment(ref signal); if (FixConnectionLossAction != null) RetryHelper.Make().Execute(FixConnectionLossAction); Interlocked.Decrement(ref signal); } continue; } catch (Exception ex) { Console.WriteLine("retry helper catch: " + ex.Message); Thread.Sleep(retryDelay); if (IfErrorThen != null) IfErrorThen(); continue; } } } }
仔細看上面代碼的朋友肯定也注意到裡面catch connectionloss exception的代碼塊中使用了Interlocked,這是因為:在多線程系統俠,如果zk串連丟失了,由於多個地方都在嘗試zk操作,所以會導致並發性的進入catch loss connection exception代碼處理塊,如果此時不加判斷的處理所有並發請求,則會出現串連多次到zk,嚴重影響效能;因此,這裡的代碼實際上意圖是將多次串連請求合并為一次串連。此處特別感謝我同事的code review,哈哈。
下面是個測試並發消除的demo,為了讓結果清晰,我把RetryHelper的catch中的Console.WriteLine注釋了
static void Main(string[] args) { RetryHelper helper=RetryHelper.Make(); helper.CreateNodeStructure = () => { Console.WriteLine("CreateNodeStructure"); }; helper.FixConnectionLossAction = () => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId+" FixConnectionLossAction BEGIN "+DateTime.Now.ToString()); Thread.Sleep(2000); Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " FixConnectionLossAction END " + DateTime.Now.ToString()); }; helper.IfErrorThen = () => { Console.WriteLine("IfErrorThen"); }; var tasks=new List<Task>(); for (int i = 0; i < 10; i++) { var task = new Task(() => { helper.Execute(() => { throw new ZooKeeperNet.KeeperException.ConnectionLossException(); }); }); tasks.Add(task); } tasks.ForEach(t=>t.Start()); Task.WaitAll(tasks.ToArray()); Console.ReadKey(); }
運行:
code download