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,但接口日志使用就不合适。