工作踩坑记录

本文最后更新于:2022年2月27日 晚上

工作踩坑记录

1. go截取字符串后乱码

在做项目时,用到go string字符串的索引来截取字符串,截取后出现了乱码

1
2
3
str := "深圳"

str1 := str[0:1] // 想截取出“深”但得到的是�

经验证后发现,go语言中string底层是byte切片,中文字体用3byte存储,而英文和数字用1byte存储,通过索引切片的模式自然会截取出中文编码的一部分,因此造成乱码的结果

image-20211117144416660

解决方案:转成rune切片后索引操作

image-20211117144924443

2. panic: runtime error: invalid memory address or nil pointer dereference

这个就类似于java中的空指针异常NullPointerException,在声明了指针变量之后未分配内存而直接使用

image-20211117145707339

解决办法:可以通过new操作来为对应的类型分配内存,并返回对应类型的指针:

1
2
3
4
5
ptr = new(int)
// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
// func new(Type) *Type

在实际项目中,我们一般不对基本类型操作指针而是传值,对结构体使用指针较为多,但通过new的操作较少

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
stu := new(Student)
stu.name = "张三"
fmt.Println(stu)

stu2 := &Student{name: "李四"} // 实际工作中常用这种方式
fmt.Println(stu2)
}

type Student struct {
name string
}

3. WaitGroup使用错误

在一次做需求中,由于doSomeThing() 函数比较耗时,因此采用协程并发的方式运行,写出了如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
wg = &sync.WaitGroup{}

for i := range sizeList {
i := i
go func() {
wg.Add(1) // 1
defer wg.Done()
doSomeThing(i) // 一些耗时操作
}()
}

wg.Wait() // 2
return

然而,程序并不会执行len(sizeList)doSomeThing() 函数,原因在于wg.Add(1)写在了go func(){}内部,正确的写法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
wg = &sync.WaitGroup{}

for i := range sizeList {
i := i
wg.Add(1)
go func() {
defer wg.Done()
doSomeThing(i) // 一些耗时操作
}()
}

wg.Wait()
return

错误的写法中,可能主goroutine已经执行到了wg.Waig()而协程还没有启动,因此主线程会直接继续执行wg.Waig()后续的代码,从而使wg.Waig()失效

4. go slice扩容引发的坑

golang 中的 slice 是引用类型,也就是传递变量的过程中是传递指针,比如如下代码

1
2
3
4
5
6
s1 := []int{1, 2, 3, 4}
s2 := s1
s1[0] = 100

fmt.Println("s1=", s1) //s1= [100 2 3 4]
fmt.Println("s2=", s2) //s2= [100 2 3 4]

变量 s2 和变量 s1 指向的是内存中的同一个地址,所以修改 s1 内元素的同时也会对 s2 进行修改

1
2
3
4
5
6
7
8
9
10
11
s1 := []int{1, 2, 3, 4} // len = 4, cap = 4
s2 := s1 // s2 和 s1 指向同一个地址
s1 = append(s1, 5) // s1 发生扩容, 此时 cap = 8, s1 指向新的地址, 4个元素copy到新的地址并添加元素5
s3 := s1 // 此时 s3 和 s1 是同一个地址,s2是原来的地址
s1 = append(s1, 6) // append 操作会将 s1 的 len+1 虽然 s1 和 s3 指向的内存地址一样,但len不同 所以打印的元素不同

s1[0] = 100
fmt.Println("s1=", s1) // s1= [100 2 3 4 5 6]
fmt.Println("s2=", s2) // s2= [1 2 3 4]
fmt.Println("s3=", s3) // s3= [100 2 3 4 5]

image-20220227174922677

5. panic: proto: message data_collection_protos.PerformanceReq is already registered

原因是ugc_control服务中引用了utils服务,utils服务引用middlewares版本为v1.1.2,ugc_control服务中引用的最新版本,不同版本的middlewares导致 go 认为是两个包,从而重复注册两个data_collection_protos导致panic

解决:首先是按照网上的方法降级到指定的版本避免panic,但是这样做的话服务会不断的重启,只好从根源解决重复注册问题,将项目中通过其他服务间接用到的middlewares全部升级为最新版本

https://developers.google.com/protocol-buffers/docs/reference/go/faq#namespace-conflict


工作踩坑记录
https://yangshuai-uestc.github.io/2021/11/17/工作踩坑记录/
作者
Catsyang
发布于
2021年11月17日
许可协议