记一次提升18倍的性能优化
以下文章来源于捉虫大师 ,作者捉虫大师
背景
Consumer 和 Provider 的服务发现请求(注册、注销、订阅)都发给 Agent,由它全权代理 Registry 和 Agent 保持 Grpc 长链接,长链接的目的主要是 Provider 方有变更时,能及时推送给相应的 Consumer。为了保证数据的正确性,做了推拉结合的机制,Agent 会每隔一段时间去 Registry 拉取订阅的服务列表 Agent 和业务服务部署在同一台机器上,类似 Service Mesh 的思路,尽量减少对业务的入侵,这样就能快速的迭代了
寻找优化点
AssembleCategoryProviders
方法,与其直接关联的是2个 redis 相关的方法 1个叫 assembleUrlWeight
的方法
func AssembleUrlWeight(rawurl string, lidcWeight int) string {
u, err := url.Parse(rawurl)
if err != nil {
return rawurl
}
values, err := url.ParseQuery(u.RawQuery)
if err != nil {
return rawurl
}
if values.Get("lidc_weight") != "" {
return rawurl
}
endpointWeight := 100
if values.Get("weight") != "" {
endpointWeight, err = strconv.Atoi(values.Get("weight"))
if err != nil {
endpointWeight = 100
}
}
values.Set("weight", strconv.Itoa(lidcWeight*endpointWeight))
u.RawQuery = values.Encode()
return u.String()
}
#
后面的片段部分。lidc_weight
和 weight
参数。Shannon(香农)
借鉴了热力学的概念,把信息中排除了冗余后的平均信息量称为信息熵
。优化
优化获取 url 参数性能
dubbo://127.0.0.1:20880/org.newboo.basic.MyDemoService?weight=100&... dubbo://127.0.0.1:20880/org.newboo.basic.MyDemoService?xx=yy&weight=100&...
&weight=
,要么是 ?weight=
,结束要么是&
,要么直接到字符串尾,代码就很好写了,先手写个解析参数的算法:func GetUrlQueryParam(u string, key string) (string, error) {
sb := strings.Builder{}
sb.WriteString(key)
sb.WriteString("=")
index := strings.Index(u, sb.String())
if (index == -1) || (index+len(key)+1 > len(u)) {
return "", UrlParamNotExist
}
var value = strings.Builder{}
for i := index + len(key) + 1; i < len(u); i++ {
if i+1 > len(u) {
break
}
if u[i:i+1] == "&" {
break
}
value.WriteString(u[i : i+1])
}
return value.String(), nil
}
func getParamByUrlParse(ur string, key string) string {
u, err := url.Parse(ur)
if err != nil {
return ""
}
values, err := url.ParseQuery(u.RawQuery)
if err != nil {
return ""
}
return values.Get(key)
}
func BenchmarkGetQueryParam(b *testing.B) {
for i := ; i < b.N; i++ {
getParamByUrlParse(u, "anyhost")
getParamByUrlParse(u, "version")
getParamByUrlParse(u, "not_exist")
}
}
func BenchmarkGetQueryParamNew(b *testing.B) {
for i := ; i < b.N; i++ {
GetUrlQueryParam(u, "anyhost")
GetUrlQueryParam(u, "version")
GetUrlQueryParam(u, "not_exist")
}
}
BenchmarkGetQueryParam-4 103412 9708 ns/op
BenchmarkGetQueryParam-4 111794 9685 ns/op
BenchmarkGetQueryParam-4 115699 9818 ns/op
BenchmarkGetQueryParamNew-4 2961254 409 ns/op
BenchmarkGetQueryParamNew-4 2944274 406 ns/op
BenchmarkGetQueryParamNew-4 2895690 405 ns/op
strings.Builder
,这也是实际测试的结果,使用 +
或者 fmt.Springf
性能都没这个好,感兴趣可以测试下看看。优化 url 写入参数性能
func AssembleUrlWeightNew(rawurl string, lidcWeight int) string {
if lidcWeight == 1 {
return rawurl
}
lidcWeightStr, err1 := GetUrlQueryParam(rawurl, "lidc_weight")
if err1 == nil && lidcWeightStr != "" {
return rawurl
}
var err error
endpointWeight := 100
weightStr, err2 := GetUrlQueryParam(rawurl, "weight")
if weightStr != "" {
endpointWeight, err = strconv.Atoi(weightStr)
if err != nil {
endpointWeight = 100
}
}
if err2 != nil { // url中不存在weight
finUrl := strings.Builder{}
finUrl.WriteString(rawurl)
if strings.Contains(rawurl, "?") {
finUrl.WriteString("&weight=")
finUrl.WriteString(strconv.Itoa(lidcWeight * endpointWeight))
return finUrl.String()
} else {
finUrl.WriteString("?weight=")
finUrl.WriteString(strconv.Itoa(lidcWeight * endpointWeight))
return finUrl.String()
}
} else { // url中存在weight
oldWeightStr := strings.Builder{}
oldWeightStr.WriteString("weight=")
oldWeightStr.WriteString(weightStr)
newWeightStr := strings.Builder{}
newWeightStr.WriteString("weight=")
newWeightStr.WriteString(strconv.Itoa(lidcWeight * endpointWeight))
return strings.ReplaceAll(rawurl, oldWeightStr.String(), newWeightStr.String())
}
}
url 本身不存在 weight 参数,则直接在 url 后拼接一个 weight 参数,当然要注意是否存在 ?
url 本身存在 weight 参数,则直接进行字符串替换
lidcWeight = 1
时,直接返回,因为 lidcWeight = 1
时,后面的计算其实都不起作用(Dubbo 权重默认为100),索性别操作,省点 CPU。func BenchmarkAssembleUrlWeight(b *testing.B) {
for i := ; i < b.N; i++ {
for _, ut := range []string{u, u1, u2, u3} {
AssembleUrlWeight(ut, 60)
}
}
}
func BenchmarkAssembleUrlWeightNew(b *testing.B) {
for i := ; i < b.N; i++ {
for _, ut := range []string{u, u1, u2, u3} {
AssembleUrlWeightNew(ut, 60)
}
}
}
BenchmarkAssembleUrlWeight-4 34275 33289 ns/op
BenchmarkAssembleUrlWeight-4 36646 32432 ns/op
BenchmarkAssembleUrlWeight-4 36702 32740 ns/op
BenchmarkAssembleUrlWeightNew-4 573684 1851 ns/op
BenchmarkAssembleUrlWeightNew-4 646952 1832 ns/op
BenchmarkAssembleUrlWeightNew-4 563392 1896 ns/op
效果
后
为什么要在推送和拉取的时候去解析 url 呢?不能事先算好存起来吗? 为什么只优化了这点,其他的点是否也可以优化呢?
相关文章