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++
}
【xorm】 fatal error: sync: unlock of unlocked mutex
报错信息:
fatal error: sync: unlock of unlocked mutex
goroutine 29697 [running]:
runtime.throw(0x16ffb3c, 0x1e)
/root/.gvm/gos/go1.15/src/runtime/panic.go:1116 +0x72 fp=0xc0005ab4a0 sp=0xc0005ab470 pc=0x437892
sync.throw(0x16ffb3c, 0x1e)
/root/.gvm/gos/go1.15/src/runtime/panic.go:1102 +0x35 fp=0xc0005ab4c0 sp=0xc0005ab4a0 pc=0x467cb5
sync.(*Mutex).unlockSlow(0xc0005763d0, 0xc0ffffffff)
/root/.gvm/gos/go1.15/src/sync/mutex.go:196 +0xd8 fp=0xc0005ab4e8 sp=0xc0005ab4c0 pc=0x489a38
sync.(*Mutex).Unlock(...)
/root/.gvm/gos/go1.15/src/sync/mutex.go:190
sync.(*Map).Store(0xc0005763d0, 0x168fdc0, 0x15f4a80, 0x1622060, 0xc00bbdfb30)
/root/.gvm/gos/go1.15/src/sync/map.go:162 +0x2a5 fp=0xc0005ab590 sp=0xc0005ab4e8 pc=0x487e45
xorm.io/xorm/tags.(*Parser).ParseWithCache(0xc000576380, 0x15f4a80, 0xc00e5a2200, 0x199, 0x199, 0x20, 0x14aca20)
/go/pkg/mod/xorm.io/xorm@v1.0.7/tags/parser.go:78 +0x150 fp=0xc0005ab5f8 sp=0xc0005ab590 pc=0x72a790
xorm.io/xorm/internal/statements.(*Statement).SetRefBean(0xc00e5a8f00, 0x1445a60, 0xc00e5a2200, 0x15f4a80, 0xc00e5a2200)
/go/pkg/mod/xorm.io/xorm@v1.0.7/internal/statements/statement.go:290 +0x6c fp=0xc0005ab668 sp=0xc0005ab5f8 pc=0x751bec
xorm.io/xorm.(*Session).get(0xc00e59eb60, 0x1445a60, 0xc00e5a2200, 0xc0005ab800, 0x0, 0x0)
/go/pkg/mod/xorm.io/xorm@v1.0.7/session_get.go:43 +0xd98 fp=0xc0005ab7e0 sp=0xc0005ab668 pc=0x78c878
xorm.io/xorm.(*Session).Get(0xc00e59eb60, 0x1445a60, 0xc00e5a2200, 0xc00e5b2100, 0x0, 0x0)
/go/pkg/mod/xorm.io/xorm@v1.0.7/session_get.go:25 +0x7f fp=0xc0005ab838 sp=0xc0005ab7e0 pc=0x78ba5f
stat/pkg/repository/dmp.(*newDuration).GetByChannel(0xc00e5b2140, 0xc009627d20, 0xa, 0xc004847ad0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
/root/workspace/market_stat_iBZB/pkg/repository/dmp/new_duration.go:72 +0x24d fp=0xc0005ab8a8 sp=0xc0005ab838 pc=0xf92d4d
背景:常驻任务,执行table update操作;
解决:翻查代码,发现获取表实例非单列,每次执行都会重新获取实例,应该并发执行发生竞争,mutex锁我们知道解锁一个已经释放的锁会发生panic。表实例获取改为单列问题解决,没有发生panic。
kafka阻塞问题
消费某个分区发生阻塞不执行,也没报错;重启可以修复,但是还是会偶发;
业务背景:消费者获取消息,业务处理逻辑之后,推入另一个kafka topic,供下游处理。
问题定位在kafka发布消息出现错误,错误的chan是一个无缓冲通道,所以发生阻塞。解决:
var err error
con := sarama.NewConfig()
con.Version = sarama.V2_2_0_0
con.Producer.Partitioner = sarama.NewRandomPartitioner
con.Producer.Compression = sarama.CompressionGZIP
emrProducer, emrTopics, err = kafka.NewProducer("emr", con)
if err != nil {
panic(err)
}
// 错误channel消费
go func() {
for {
e := <-emrProducer.Errors()
check.Error(e)
}
}()