go实战中的一些坑(1)

一个关于goroutine的坑,与解决它的故事…

坑的起源

这项目是公司缺人所以我来接手整一下前端,然后在测试一个地图页面的时候感觉到明显的延迟,原本以为是d3的svg地图渲染慢,正想着要不要换成canvas来加快渲染,然后我打开了开发者工具,发现这个接口整整需要4s来获取,这可是本地测试环境啊,这要是上线来整的话那不是凉凉?逐打开项目代码查看,这个接口程序非常简单,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for _, pro := range prolist {
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)
}

看了看,这不行啊,有点点拖,然后下手改造。

开始挖坑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

for _, pro := range prolist {
wg.Add(1)
go func(){
defer wg.done()
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()

使用waitgroupgoroutine进行 一个小小的改造,应该就这样吧,没问题的,不测试了,跑起来,草,127ms ,指数级增长的快感,地图瞬间渲染完成,这感觉,太爽了,正当着想着和同事吹吹,不经意间把鼠标移到地图上,发现所有的附加数据都是同一个数字,所有城市都是同一个名字,我心一惊,凉了 ,赶紧回滚回滚测试环境。

埋坑

主要问题在于主进程与gorouite进程的通讯如果不通过chanel的话,只有第一个参数复制了进去,所以接下来的所有操作都是用同一个参数进行操作。埋坑方法如下。

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)

这样就可以实现主进程与goroutine的通信。go run .测试,没问题,add , commit ,push, merge 一梭子上线测试环境,没问题,终于舒服了。

在改造其他有同样情况的接口时,我发现有defer和没有 , 速度也是有区别的,想起之前看过的一篇文章defer在使用的时候的确会有延迟,总的来说就是靠效率来换取recover的安全…

然后又看到一篇煎鱼大大的文章 ,文章说go1.13的defer效率比以前高了大概30% ,过会升级看看。