channel 关闭导致的问题
因为向关闭的channel 中传递数据导致 panic, 代码如下:
func GetToken(ctx context.Context) (t string, err error) {
ctxTimeout, cancel := context.WithTimeout(context.TODO(), 500*time.Millisecond)
defer cancel()
var token string
signal := make(chan struct{})
defer close(signal)
go func() {
token, err = infsecc.GetToken(false)
if err != nil {
logs.CtxWarn(ctx, "get psm token error: %s", err.Error())
}
signal <- struct{}{}
}()
select {
case <-ctxTimeout.Done():
token = DefaultToken
err = errors.New("get token timeout")
logs.CtxWarn(ctx, "get token timeout")
case <-signal:
}
if len(token) == 0 {
logs.CtxWarn(ctx, "get token failed")
token = DefaultToken
}
return token, err
}
经验教训如下:
- channel 应该只能在发送端关闭;
- channel 可以不关闭,由系统自动回收;
代码修改如下:
func GetToken(ctx context.Context) (t string, err error) {
ctxTimeout, cancel := context.WithTimeout(context.TODO(), 500*time.Millisecond)
defer cancel()
var token string
signal := make(chan struct{})
go func() {
token, err = infsecc.GetToken(false)
if err != nil {
logs.CtxWarn(ctx, "get psm token error: %s", err.Error())
return
}
signal <- struct{}{}
}()
select {
case <-ctxTimeout.Done():
token = DefaultToken
err = errors.New("get token timeout")
logs.CtxWarn(ctx, "get token timeout")
case <-signal:
}
if len(token) == 0 {
logs.CtxWarn(ctx, "get token failed")
token = DefaultToken
}
return token, err
}
值传递与引用传递
list 与map 同样是引用拷贝,但二者还是有差别的;
package main
import "fmt"
import "unsafe"
func main() {
fmt.Println("Hello, 世界")
m := make(map[int]int,0)
var t map[int]int
t = m
for i:=0; i< 10000; i++ {
t[i] = i
}
fmt.Println(len(t))
fmt.Println(len(m))
l1 := make([]int, 0, 4)
l2 := l1
for i := 0; i< 10000; i++ {
l2 = append(l2, i)
}
fmt.Println(l2)
fmt.Println(l1)
// list 是一个结构体
fmt.Println(unsafe.Sizeof(l1))
fmt.Println(unsafe.Sizeof(l2))
// map 是一个指针结构
fmt.Println(unsafe.Sizeof(m))
fmt.Println(unsafe.Sizeof(t))
}
编码过程中遭遇到的问题
func ImageHandlerCommon(data []byte) map[string]interface{} {
......
// metrics monitor
defer util.HandleMonitor("image_app_load", tool.InterfaceToString(mainLog["app_id"]),
tool.InterfaceToString(mainLog["app_version"]))
......
这一句代码放在方法的最初, 发现最后执行的方法里面 :tool.InterfaceToString(mainLog[“app_version”]) 的值为空,发现是值传递导致的问题;
而后面把这一句代码放在方法的最后面,因为此时mainLog 已经处理完成,就没有空值的问题。
读写锁引入的问题
项目中对鉴权信息用一个LRU进行了缓存,LRU底层是go 源码库中包含的链表, 外部用读写锁控制访问;
写入LRU的时候,使用写锁;读取的时候,使用读锁,因为是一个LRU,读取的时候,如果在缓存,还会把Item放到链表头部;这里引入了一个潜在问题, 读取与写入偶然情况下,会同时操作底层的list, 导致读取的时候,获取的元素为空;