这篇主要是纠正上一篇的补坑法….
第一个坑 在上一篇文章我修改错误时是这么来整的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 proChan := make (chan AreaList,len (prolist)) for _, pro := range prolist { proChan <- *pro wg.Add(1 ) go func () { defer wg.done() pro :=<- proChan statisticsModel, _ := model.GetProStatitics(pro.BELONG_PRO) LineStatus := model.GetProLineStatusCount(pro.BELONG_PRO) r := model.AreaStatisticsModel{ Area: pro.BELONG_PRO, Name: pro.NAME, StatisticsModel: model.StatisticsModel{ Totalcount: statisticsModel.Totalcount, Totalanomalies: statisticsModel.Totalanomalies, TotalTunnelAnomalies: LineStatus, }, } areaStatisticsModels = append (areaStatisticsModels, r) }() } wg.wait() close (proChan)
但其实是可以不用 channel 的 , 具体方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 for _, pro := range prolist { wg.Add(1 ) go func (pro *model.AreaModel) { defer wg.done() pro :=<- proChan statisticsModel, _ := model.GetProStatitics(pro.BELONG_PRO) LineStatus := model.GetProLineStatusCount(pro.BELONG_PRO) r := model.AreaStatisticsModel{ Area: pro.BELONG_PRO, Name: pro.NAME, StatisticsModel: model.StatisticsModel{ Totalcount: statisticsModel.Totalcount, Totalanomalies: statisticsModel.Totalanomalies, TotalTunnelAnomalies: LineStatus, }, } areaStatisticsModels = append (areaStatisticsModels, r) }(pro) } wg.wait()
就完事了…. 所以说自大是真的不好,没个几年经验还是不要轻易的去帮忙修代码。
第二个坑 当对于处理少量数据的时候,使用goroutine反而会拖慢系统的速度,因为goroutine的使用是需要额外开销的。
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 func ListBroken (city int ) ([]ClientListModel, int , error ) { clientSocket := make ([]ClientListModel, 0 ) var numCount int topoJson := GetOperationalTopology() remoteServerList := GetCityClientList(city) for _, remoteServer := range remoteServerList { server := ClientListModel{ ID: remoteServer.ID, VALIAS: remoteServer.VALIAS, VXLAN_STATUS: 0 , } uid := remoteServer.UUID + "secret" for _, node := range topoJson.Array() { if !st.BoolContaits(uid, topoJson.String()) { server.VXLAN_STATUS = 2 numCount++ break } if st.BoolContaits(uid, node.Get("secret" ).String()) { count := 0 tpLines := GetJsonTerminationPoint(node) for _, tpId := range tpLines.Array() { if st.BoolContaits(remoteServer.VALIAS, tpId.Get("secret" ).String()) { bfdStatusArray := tpId.Get("secret" ) for _, bfdStatus := range bfdStatusArray.Array() { if bfdStatus.Get("secret" ).String() == "secret" && bfdStatus.Get("secret" ).String() == "secret" { count++ } if bfdStatus.Get("secret" ).String() == "secret" && bfdStatus.Get("secret" ).String() == "secret" { count++ } if bfdStatus.Get("secret" ).String() == "secret" && bfdStatus.Get("secret" ).String() == "secret" { server.VXLAN_STATUS = 1 } if count == 3 { server.VXLAN_STATUS = 2 numCount++ } } } } } } if server.VXLAN_STATUS == 0 { server.VXLAN_STATUS = 2 numCount++ } clientSocket = append (clientSocket, server) } return clientSocket, numCount, nil }
这是一段业务代码 , 如你所见…非常臃肿,在获取数据时的时候已经没了 5~7ms了 ,整段代码在测试环境跑完需要11ms左右,这还是因为需要验证的数据只有两条,生产环境下跑完这一段似乎需要整整300ms左右,所以我着手尝试改造…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 clientSocket := make ([]ClientListModel, 0 ) var numCount int topoJson := GetOperationalTopology() remoteServerList := GetCityClientList(4401 ) wg := sync.WaitGroup{} for _, remoteServer := range remoteServerList { wg.Add(1 ) go func (remoteServer *RemoteDeviceListModel) { server := ClientListModel{ ID: remoteServer.ID, VALIAS: remoteServer.VALIAS, VXLAN_STATUS: 0 , } uid := remoteServer.UUID + "secret" topoJson.ForEach(func (key, value gjson.Result) bool { if !st.BoolContaits(uid, topoJson.String()) { server.VXLAN_STATUS = 2 numCount++ return false } if !st.BoolContaits(uid, value.Get("secret" ).String()) { return true } count := 0 value.Get("secret" ).ForEach(func (key, value gjson.Result) bool { if st.BoolContaits(remoteServer.VALIAS, value.Get("secret" ).String()) { if value.Get("secret" ).String() == "secret" { switch value.Get("secret" ).String() { case "secret" , "secret" : count++ case "secret" : server.VXLAN_STATUS = 1 } } if count == 3 { server.VXLAN_STATUS = 2 numCount++ } } return true }) return true }) if server.VXLAN_STATUS == 0 { server.VXLAN_STATUS = 2 numCount++ } clientSocket = append (clientSocket, server) wg.Done() }(remoteServer) }
这是一次失败的尝试 ,在改造中,我把原来的 for range 改成了 gjson 库自带的 ForEach() 。让我们看看这段代码实现了些啥…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 func (t Result) ForEach(iterator func (key, value Result) bool ) { if !t.Exists() { return } if t.Type != JSON { iterator(Result{}, t) return } json := t.Raw var keys bool var i int var key, value Result for ; i < len (json); i++ { if json[i] == '{' { i++ key.Type = String keys = true break } else if json[i] == '[' { i++ break } if json[i] > ' ' { return } } var str string var vesc bool var ok bool for ; i < len (json); i++ { if keys { if json[i] != '"' { continue } s := i i, str, vesc, ok = parseString(json, i+1 ) if !ok { return } if vesc { key.Str = unescape(str[1 : len (str)-1 ]) } else { key.Str = str[1 : len (str)-1 ] } key.Raw = str key.Index = s } for ; i < len (json); i++ { if json[i] <= ' ' || json[i] == ',' || json[i] == ':' { continue } break } s := i i, value, ok = parseAny(json, i, true ) if !ok { return } value.Index = s if !iterator(key, value) { return } } }
这次改造后,反而比原先的实现多了2ms左右时间开销。
总之就是为了兼容性多了很多对当前环境不必要的实现 ,多了一笔可观的消耗。虽然写的时候似乎比for range 美观(其实也没美观多少),但其实是非常的得不偿失的。而且其实在使用.Array()之后, 我们得到的是一个slice 对象,而不是map 对象 ,所以我们其实可以使用for i:=0;i<len(slice);i++ 来实现遍历,这样子虽然看着丑,但是性能实现其实比使用 for range 高整整一倍。for range比常规循环的具体原因是,它在实现遍历的同时,还需要对循环元素进行拷贝,而slice[i]是直接指针索引,性能肯定比拷贝快得多(参考源 )。
【修正 : for range 可以直接只把index 索引出来。 】
然后我把代码修改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 clientSocket := make ([]ClientListModel, 0 ) var numCount int topoJson := GetOperationalTopology() remoteServerList := GetCityClientList(4401 ) wg := sync.WaitGroup{} for _, remoteServer := range remoteServerList { wg.Add(1 ) go func (remoteServer *RemoteDeviceListModel) { server := ClientListModel{ ID: remoteServer.ID, VALIAS: remoteServer.VALIAS, VXLAN_STATUS: 0 , } uid := remoteServer.UUID + "secret" for i := range topoJson.Array() { if !st.BoolContaits(uid, topoJson.String()) { server.VXLAN_STATUS = 2 numCount++ break } if !st.BoolContaits(uid, topoJson.Array()[i].Get("secret" ).String()) { break } count := 0 for j := range topoJson.Array()[i].Get("secret" ).Array() { value := topoJson.Array()[i].Get("secret" ).Array()[j] if st.BoolContaits(remoteServer.VALIAS, value.Get("secret" ).String()) { if value.Get("secret" ).String() == "secret" { switch value.Get("secret" ).String() { case "secret" , "secret" : count++ case "secret" : server.VXLAN_STATUS = 1 } } if count == 3 { server.VXLAN_STATUS = 2 numCount++ } } } } if server.VXLAN_STATUS == 0 { server.VXLAN_STATUS = 2 numCount++ } clientSocket = append (clientSocket, server) wg.Done() }(remoteServer) } wg.Wait()
这样子,就比原先的实现还少了2ms , 这还是使用了gorouite的情况下。