09.codex典型问题整改总结.md - wwj-2017-1117/AES_OK GitHub Wiki
codex典型问题整改总结
##1. tag: json和yaml
背景:整改fsadmin服务过程中,修改字段大小写,导致应用部署失败,解决办法: yaml序列化和反序列化最好添加tag. 例如:
type DemoData struct {
ID int
Name string
}
var demoData = DemoData{
ID: 123456,
Name: "Api-server",
}
func main() {
jsonFunc()
yamlFunc()
}
func jsonFunc() {
data, err := json.Marshal(&demoData)
if err != nil {
fmt.Printf("error: %v", err)
return
}
fmt.Println(string(data)) // 默认所有保持不变{"ID":123456,"Name":"Api-server"}
}
func yamlFunc() {
data, err := yaml.Marshal(&demoData)
if err != nil {
fmt.Printf("error: %v", err)
return
}
fmt.Println(string(data)) // 默认所有字母变小写 "id":123456,"name":"Api-server"
}
// 最佳方式: json和 yaml添加2个tag
type DemoData struct {
ID int `json:"id"" yaml:"id""`
Name string `json:"name"" yaml:"name""`
}
##2.切片append和copy 在排查代码的过程中,发现了切片在append和copy两种场景下,对length参数的不同要求,总结如下:
// 2.1 切片append错误实例
func slice1(){
s1 := make([]string, 16, 16)
s2 := []string{"abc"}
s1 = append(s1, s2...)
fmt.Println(s1) //[ abc],大片的空白
}
// 2.2 切片append正确实例
func slice1(){
s1 := make([]string, 0, 16) // 容量C也要指定
s2 := []string{"abc"}
s1 = append(s1, s2...)
fmt.Println(s1) //[ abc]
}
// 2.3 切片copy错误实例
func slice2(){
s1 := []string{"abc"}
s2 := make([]string, 0, len(s1))
copy(s2,s1)
fmt.Println(s2) // 输出[],不符合预期
}
// 2.4 切片copy正确实例
func slice2(){
s1 := []string{"abc"}
s2 := make([]string, len(s1), len(s1)) // 指定参数 length
copy(s2,s1)
fmt.Println(s2) // // 输出[abc],符合预期
}
##3.time.after 正确使用方法 time.after()通常用于超时场景下,但是不正确的使用容易买下潜在的缺陷,如下所示:
// 3.1 time.after 不正确的使用会导致内存泄露。
func time1() {
for {
select {
case <-time.After(time.Millisecond): // 总是创建新对象会造成内存泄露
println("time out, and end")
case <-ch:
println("ch ")
}
}
}
// 3.2 time.after 正确的使用方法
func time2(){
timer := time.NewTimer(time.Millisecond)
for {
timer.Reset(time.Millisecond) // 总是复用这一个对象,避免内存泄露;
select {
case _, ok := <-timer.C: // comma,ok 防止死循环
if !ok {
println("timer stoped")
return
}
println("time out") // 超时1 mils 才会有输出;
case <-ch:
println("ch")
}
}
}
##4.hamc-hash认证码 HMAC通常用于密钥相关的哈希运算消息认证码,golang标准库-crypto/hmac(加密哈希算法)也提供了相关功能。 但不正确的使用容易引入安全问题,如下所示:
//
func hmacFunc() {
// 不正确的做法: 使用md5算法进行hash加密,128位(16字节)不安全
hash = hmac.New(md5.New, []byte("abc123")) // 创建对应的md5哈希加密算法
fmt.Printf("%x\n", hash.Sum(nil)) // 0eee86e484505ec4ab48c18095e6a8ac
// 推荐做法: 使用sha256算法进行hash加密, 推荐
hash := hmac.New(sha256.New, []byte("abc123")) // 创建对应的sha256哈希加密算法
fmt.Printf("%x\n", hash.Sum(nil)) // c10a04b78bcbcc1c4cba37f6afe0fa60cbf08f6e0a1d93b09387f7069be1aeff
}
##5.binary库字节序 使用注意事项
// go binary库中的字节序(Byte Order)问题
// 大字节序(big endian)和 小字节序(little endian)
// 大端转换,尽管编译器已经帮忙检查了bouds,但还是要排查下数组下标是否会越界。
func (bigEndian) Uint32(b []byte) uint32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
}
##6. 字符串拼接优化 字符串拼接优先考虑bytes.Buffer。 Golang字符串拼接常见有如下方式:
fmt.Sprintf
strings.Join
string +
bytes.Buffer
fmt.Sprintf会动态解析参数,效率通常是最差的,而string是只读的,string+会导致多次对象分配与值拷贝,而bytes.Buffer在预设大小情况下,通常只会有一次拷贝和分配,不会重复拷贝和复制,故效率是最佳的。 推荐做法:优先使用bytes.Buffer,非关键路径,若考虑简洁,可考虑其它方式,比如错误日志拼接使用fmt.Sprintf,但接口日志使用就不合适。