Go 注意坑

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++
        }
posted @ 2020-03-01 15:05  walkingSun  阅读(268)  评论(1编辑  收藏  举报
**/