• 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++
            }
    
  • 相关阅读:
    stylus 安装
    Vue中img的src属性绑定与static文件夹
    vue-cli的build的文件夹下没有dev-server.js文件,怎么配置mock数据
    easyui中的datebox空间,起始时间不受限制,终止时间不能选择起始时间选中的时间靠前的时间
    python实现文件下载图片视频
    python爬虫 urllib库基本使用
    python面向对象的三大特征
    常见网页状态码
    说说为什么会有ssl.CertificateError报错
    python-魔术方法
  • 原文地址:https://www.cnblogs.com/followyou/p/12389880.html
Copyright © 2020-2023  润新知