rpc、sync.Pool学习

alex_023 · · 3730 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

前段时间,因为rpc使用的conn阻塞,导致了一个非常意外的问题,静下心来学习了go的rpc包的代码。梳理了一下运行逻辑: 1. 客户端Client执行Call发出请求(内部为Call-》Go-》send)将请求向服务端打包。每次请求,都写入一个Request头,而具体业务则作为Body。 2. 服务端Server得到数据,先读取Request,并根据Request中的ServiceMethod属性,寻找之前注册的Service方法,并运行。 3. 得到的结果后,server调用call,将数据回传。数据写入之前,会根据Request对应给出Response来作为消息头。 在了解代码的过程中,发现server的代码或许是为了减少request、response头的内存GC,通过指针的方式做了一个简单的pool。 ** Request定义 ** ``` code // Request is a header written before every RPC call. It is used internally // but documented here as an aid to debugging, such as when analyzing // network traffic. type Request struct { ServiceMethod string // format: "Service.Method" Seq uint64 // sequence number chosen by client next *Request // for free list in Server } ``` ** Server对Request的调用与释放 ** ``` code func (server *Server) getRequest() *Request { server.reqLock.Lock() req := server.freeReq if req == nil { req = new(Request) } else { server.freeReq = req.next *req = Request{} } server.reqLock.Unlock() return req } func (server *Server) freeRequest(req *Request) { server.reqLock.Lock() req.next = server.freeReq server.freeReq = req server.reqLock.Unlock() } ``` ### 利用sync.Pool来对比源码的指针池 如果不采用指针,而使用sync.Pool,会怎么样呢?参考Pool源码,由于使用了CPU的Cache,应该速度会快一些。简单写了一个测试代码: ``` code package buforpoint import ( "sync" ) type Person struct{ Name string Age int Next *Person } func (p *Person)ChangeAge(newage int){ //do null func p.Age=newage } func (p *Person)Report(print bool){ if print { fmt.Printf("%+v\n",p) } } type BufStruct struct{ pool sync.Pool } func NewBufStruct()*BufStruct{ return &BufStruct{ pool:sync.Pool{ New:func()interface{}{ return new(Person) }, }, } } func (b *BufStruct)Get()*Person{ p:= b.pool.Get().(*Person) *p=Person{} return p } func (b *BufStruct)Put(p *Person){ b.pool.Put(p) } type PointStruct struct{ rwmut sync.RWMutex freePerson *Person } func (p *PointStruct)Get()*Person { p.rwmut.Lock() person :=p.freePerson if person ==nil{ person =&Person{Name:"Alex_023",Age:38} }else{ p.freePerson = person.Next *person =Person{} //重新赋值 } p.rwmut.Unlock() return person } func (p *PointStruct)Put(person *Person){ p.rwmut.Lock() person.Next=p.freePerson p.freePerson =person p.rwmut.Unlock() } func NewPointStruct()*PointStruct{ return &PointStruct{} } ``` ``` code package buforpoint import "testing" func TestBufStruct(t *testing.T) { bs:= NewPointStruct() person:=bs.Get() person.ChangeAge(2) person.Report() bs.Put(person) } func TestPointStruct(t *testing.T) { ps:=NewPointStruct() person:=ps.Get() person.ChangeAge(2) person.Report() ps.Put(person) } func BenchmarkBufStruct(b *testing.B) { b.ReportAllocs() bs:= NewBufStruct() for i:=0;i<b.N;i++{ person:=bs.Get() person.ChangeAge(i) person.Report() bs.Put(person) } } func BenchmarkPointStruct(b *testing.B) { b.ReportAllocs() ps := NewPointStruct() for i:=0;i<b.N;i++{ person:= ps.Get() person.ChangeAge(i) person.Report() ps.Put(person) } } ``` 测试结果区别还是很明显,利用sync.Pool会有大幅提升: ``` code /usr/local/go/bin/go test -v forstudy/buforpoint -bench "^BenchmarkBufStruct|BenchmarkPointStruct$" -run ^$ PASS BenchmarkBufStruct-4 50000000 29.2 ns/op 0 B/op 0 allocs/op BenchmarkPointStruct-4 20000000 80.3 ns/op 0 B/op 0 allocs/op ok forstudy/buforpoint 3.047s ``` 当然,源代码中,指针池并不是瓶颈,更多消耗在于业务处理,这里只是因为兴趣做了个了解。

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3730 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传