diff --git a/cluster/router/chain/chain.go b/cluster/router/chain/chain.go index 3f8813631c1e6ae183a6fd435afbc9695332d9b6..f8a27ee615aed180af5210775479f0d74d988f2f 100644 --- a/cluster/router/chain/chain.go +++ b/cluster/router/chain/chain.go @@ -18,7 +18,7 @@ package chain import ( - "math" + "reflect" "sort" "sync" "sync/atomic" @@ -137,12 +137,25 @@ func (c *RouterChain) loop() { // copyRouters make a snapshot copy from RouterChain's router list. func (c *RouterChain) copyRouters() []router.PriorityRouter { - l := len(c.routers) - rs := make([]router.PriorityRouter, l, int(math.Ceil(float64(l)*1.2))) c.mutex.RLock() - copy(rs, c.routers) + ret := copySlice(c.routers) c.mutex.RUnlock() - return rs + return ret.([]router.PriorityRouter) +} + +// copyInvokers copies a snapshot of the received invokers. +func (c *RouterChain) copyInvokers() []protocol.Invoker { + c.mutex.RLock() + ret := copySlice(c.invokers) + c.mutex.RUnlock() + return ret.([]protocol.Invoker) +} + +func copySlice(s interface{}) interface{} { + t, v := reflect.TypeOf(s), reflect.ValueOf(s) + c := reflect.MakeSlice(t, v.Len(), v.Len()) + reflect.Copy(c, v) + return c.Interface() } // loadCache loads cache from sync.Value to guarantee the visibility @@ -161,9 +174,7 @@ func (c *RouterChain) buildCache() { return } - // FIXME: should lock here, it is fine with dirty read if no panic happens I believe. - invokers := make([]protocol.Invoker, len(c.invokers)) - copy(invokers, c.invokers) + invokers := c.copyInvokers() cache := BuildCache(invokers) origin := c.loadCache()