内容简介:golang在http.Request中提供了一个Context用于存储kv对,我们可以通过这个来存储请求相关的数据。在请求入口,我们把唯一的requstID存储到context中,在后续需要调用的地方把值取出来打印。如果日志是在controller中打印,这个很好处理,http.Request是作为入参的。但如果是在更底层呢?比如说是在model甚至是一些工具类中。我们当然可以给每个方法都提供一个参数,由调用方把context一层一层传下来,但这种方式明显不够优雅。想想java里面是怎么做的--每个go
开篇
golang在http.Request中提供了一个Context用于存储kv对,我们可以通过这个来存储请求相关的数据。在请求入口,我们把唯一的requstID存储到context中,在后续需要调用的地方把值取出来打印。如果日志是在controller中打印,这个很好处理,http.Request是作为入参的。但如果是在更底层呢?比如说是在model甚至是一些 工具 类中。我们当然可以给每个方法都提供一个参数,由调用方把context一层一层传下来,但这种方式明显不够优雅。想想 java 里面是怎么做的-- ThreadLocal 。虽然golang官方不太认可这种方式,但是我们今天就是要基于goroutine id实现它。
We wouldn't even be having this discussion if thread local storage wasn't useful. But every feature comes at a cost, and in my opinion the cost of threadlocals far outweighs their benefits. They're just not a good fit for Go.
思路
每个goroutine有一个唯一的id,但是被隐藏了,我们首先把它暴露出来,然后建立一个map,用id作为key,goroutineLocal存储的实际数据作为value。
获取goroutine id
1.修改 $GOROOT/src/runtime/proc.go 文件,添加 GetGoroutineId() 函数
func GetGoroutineId() int64 { return getg().goid }
其中getg()函数是获取当前执行的g对象,g对象包含了栈,cgo信息,GC信息,goid等相关数据,goid就是我们想要的。
2.重新编译源码
cd ~/go/src GOROOT_BOOTSTRAP='/Users/qiuxudong/go1.9' ./all.bash
实现 GoroutineLocal
package goroutine_local import ( "sync" "runtime" ) type goroutineLocal struct { initfun func() interface{} m *sync.Map } func NewGoroutineLocal(initfun func() interface{}) *goroutineLocal { return &goroutineLocal{initfun:initfun, m:&sync.Map{}} } func (gl *goroutineLocal)Get() interface{} { value, ok := gl.m.Load(runtime.GetGoroutineId()) if !ok && gl.initfun != nil { value = gl.initfun() } return value } func (gl *goroutineLocal)Set(v interface{}) { gl.m.Store(runtime.GetGoroutineId(), v) } func (gl *goroutineLocal)Remove() { gl.m.Delete(runtime.GetGoroutineId()) }
简单测试一下
package goroutine_local import ( "testing" "fmt" "time" "runtime" ) var gl = NewGoroutineLocal(func() interface{} { return "default" }) func TestGoroutineLocal(t *testing.T) { gl.Set("test0") fmt.Println(runtime.GetGoroutineId(), gl.Get()) go func() { gl.Set("test1") fmt.Println(runtime.GetGoroutineId(), gl.Get()) gl.Remove() fmt.Println(runtime.GetGoroutineId(), gl.Get()) }() time.Sleep(2 * time.Second) }
可以看到结果
5 test0 6 test1 6 default
内存泄露问题
由于跟goroutine绑定的数据放在goroutineLocal的map里面,即使goroutine销毁了数据还在,可能存在内存泄露,因此不使用时要记得调用Remove清除数据
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 【安全帮】FBI网站被黑:黑客获取100万条特工信息;服务器进水导致源代码丢失,软件公司起诉物业索赔...
- 代码审计--源代码审计思路
- 如何阅读源代码
- 开放源代码库指南
- unittest 源代码阅读
- 扒一扒 EventServiceProvider 源代码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Automate This
Christopher Steiner / Portfolio / 2013-8-9 / USD 25.95
"The rousing story of the last gasp of human agency and how today's best and brightest minds are endeavoring to put an end to it." It used to be that to diagnose an illness, interpret legal docume......一起来看看 《Automate This》 这本书的介绍吧!