http请求后需关闭句柄
大量请求没有关闭,会造成go的内存泄露。这也是平时编码习惯没有养成,需谨记。
务必请求后释放资源:
response.Body.Close()
解析请求参数中带”;“,解析出错
http://tj-adc.wtzw.com/click?source=wolong12;123344&project=reader_free&callback=__CALLBACK_URL__&channel=qi-guanfang_hc&ua=Dalvik%5C/2.1.0%20%28Linux;%20U;%20Android%209;%20V1813BA%20Build%5C/PKQ1.181030.001%29
解析请求的时候发现ua参数“;”开始之后的字符串都被过滤了。
查看 net/url包的源码,
...
key := query
if i := strings.IndexAny(key, "&;"); i >= 0 {
key, query = key[:i], key[i+1:]
} else {
query = ""
}
...
发现go在解析参数的时候按照&和;分割。
解决:
// 对;进行url编码,再次解析
rawQuery := ctr.Ctx.Request.URL.RawQuery
if strings.Contains(rawQuery, ";") {
rawQuery = strings.Replace(rawQuery, ";", "%3B", -1)
paramsRaw, errs := url.ParseQuery(rawQuery)
if errs == nil {
for k, v := range paramsRaw {
params.Set(k, v[0])
}
}
}
最有效的解决方案是url编码规范化。
时区
t, err := time.Parse("2006-01-02 15:04:05", time.Now().Format("2006-01-02 15:04:05"))
fmt.Println(t)
结果:
// 假设当前时间 2017-12-03 12:00:00 +0000 UTC
2020-03-09 20:00:00 +0000 UTC
发现时间多了8个小时
在windows下,time.Parse()的时区和time.Format()的时区是一致的。
但是在linux环境下,time.Parse()的默认时区是UTC,time.Format()的时区默认是本地,两者如果不处理好就会导致错误
解决:
使用time.ParseInLocation()而不是time.Parse():
t, _ := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02 15:04:05"), time.Local)
map顺序遍历
map遍历的顺序跟添加的顺序无关,随机输出。如果逻辑需要顺序输出,需要注意这个点,避免发生错误。
描述个场景,签名的生成是根据map添加的顺序。
m := make(map[string]string)
m["a"] = "1"
m["b"] = "2"
m["c"] = "3"
m["d"] = "4"
m["e"] = "5"
for k,v := range m{
fmt.Println(k,v)
}
keys := []string{"a","b","c","d","e"}
signStr := ""
salt := "qimao"
for _,k := range keys {
signStr += m[k] + "_"
}
sign := md5.Sum([]byte(signStr + salt))
fmt.Println(fmt.Sprintf("%x",sign))
另外对map中key排序,也参照使用切片排序遍历。
gorutine使用注意
...
page := 1
size := 1000
for {
// 查询1000数量数据
list := user.CheckList(project, date, size, page)
if len(list) == 0 {
break
}
group := sync.WaitGroup{}
// 活跃用户遍历
for _, uid := range list {
group.Add(1)
// 起1000个gorutine,用户信息推到mq队列
go func(uid string) {
...
err1 := mq.Push(jsons)
utils.CheckError(err1, false)
...
group.Done()
}(uid)
}
group.Wait()
page++
执行的时候,mq会出现超时等待,重试。原因是mq的连接数不够用,旧的连接没有被释放,一直等待重试。
改良下代码,使用chan阻塞执行(类似令牌桶):
...
page := 1
size := 50000
for {
// 查询50000数量活跃用户
list := user.CheckList(project, date, size, page)
if len(list) == 0 {
break
}
group := sync.WaitGroup{}
buckets := make(chan bool, 500) // 桶的容量是500
for _, uid := range list {
buckets <- true // 桶中入“令牌”,如果桶塞满,阻塞等待
group.Add(1)
go func(uid string) {
...
err1 := mq.Push(jsons)
utils.CheckError(err1, false)
...
<-buckets // 业务处理,桶中取出令牌
group.Done()
}(uid)
}
group.Wait()
page++
}