之前一致特征系统在 QA 同窗的协助下停止了一些压测,发现了一些成绩,这些成绩是较为通用的成绩,收回来给其他同窗参考一下,避免踩异样的坑。
底层依赖 sync.Pool 的场景
有一些开源库,为了优化功用,运用了官方提供的 sync.Pool,比如我们运用的 https://github.com/valyala/fasttemplate 这个库,每当你执行下面这样的代码的时分:
template := "{{host}}/?q={{query}}&foo={{bar}}{{bar}}"
t := fasttemplate.New(template, "{{", "}}")
s := t.ExecuteString(map[string]interface{}{
"host": "谷歌.com",
"query": url.QueryEscape("hello=world"),
"bar": "foobar",
})
fmt.Printf("%s", s)
外部都会生成一个 fasttemplate.Template 对象,并带有一个 byteBufferPool 字段:
type Template struct {
template string
startTag string
endTag string
texts [][]byte
tags []string
byteBufferPool bytebufferpool.Pool ==== 就是这个字段
}
byteBufferPool 底层就是经过封装的 sync.Pool:
type Pool struct {
calls [steps]uint64
calibrating uint64
defaultSize uint64
maxSize uint64
pool sync.Pool
}
这种设计会带来一个成绩,假设运用方每次央求都 New 一个 Template 对象。并停止求值,比如我们最后的用法,在每次拿到了用户的央求之后,都会用参数填入到模板:
func fromTplToStr(tpl string, params map[string]interface{}) string {
tplVar := fasttemplate.New(tpl, `{{`, `}}`)
res := tplVar.ExecuteString(params)
return res
}
在模板求值的时分:
func (t *Template) ExecuteFuncString(f TagFunc) string {
bb := t.byteBufferPool.Get()
if _, err := t.ExecuteFunc(bb, f); err != nil {
panic(fmt.Sprintf("unexpected error: %s", err))
}
s := string(bb.Bytes())
bb.Reset()
t.byteBufferPool.Put(bb)
return s
}
会对该 Template 对象的 byteBufferPool 停止 Get,在运用完之后,把 ByteBuffer Reset 再放回到对象池中。但成绩在于,我们的 Template 对象本身并没有停止复用,所以这里的 byteBufferPool 本身的作用其实并没有发扬出来。
相反的,由于每一个央求都需求重生成一个 sync.Pool,在高并发场景下,执行时会卡在 bb := t.byteBufferPool.Get() 这一句上,经过压测可以比较快地发现成绩,到达一定 QPS 压力时,会有少量的 Goroutine 堆积,比如下面有 18910 个 G 堆积在抢锁代码上:
goroutine profile: total 18910
18903 @ 0x102f20b 0x102f2b3 0x103fa4c 0x103f77d 0x10714df 0x1071d8f 0x1071d26 0x1071a5f 0x12feeb8 0x13005f0 0x13007c3 0x130107b 0x105c931
(责任编辑:admin)