Go 语言小细节(二)


数组用于函数传参时是值复制
方法或函数调用时,传入参数都是值复制,跟赋值一致,除非是 map、slice、channel、指针类型这些特殊类型是引用传递。

x := [3]int{1,2,3}

数组在函数中传参是值复制

func(arr [3]int) {
    arr[0] = 7
    fmt.Println(arr)
    // [7 2 3]
}(x)
fmt.Println(x)
// [1 2 3]

使用数组指针实现引用传参

func(arr *[3]int) {
    (*arr)[0] = 7
    fmt.Println(arr)
    // &[7 2 3]
}(&x)
fmt.Println(x)
// [7 2 3]

阅读全文

Go 语言小细节(一)


多个 defer 出现的时候,多个 defer 之间按照 LIFO(后进先出)的顺序执行

package main
import "fmt"
func main(){
    defer func(){
        fmt.Println("1")
    }()
    defer func(){
        fmt.Println("2")
    }()
    defer func(){
        fmt.Println("3")
    }()
}

对应的输出是:

3
2
1

阅读全文

Go http client 发送 get post 请求


引入包

import (
    "net/http"
)

发送 get 请求

res, _ := http.Get(getURL)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(string(body))

发送 post 请求

// 将 map 转换成 json
requestJSON, _ := json.Marshal(requestMap)
req, _ := http.NewRequest("POST", postURL, strings.NewReader(string(requestJSON)))
// 设置请求头
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
    fmt.Println(err)
}
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(string(body))
阅读全文

Go 处理时间


package main

import (
    "fmt"
    "time"
)

func main() {
    timeNow := time.Now()
    fmt.Println("tNow(time format): ", timeNow)
    
    // 时间转化为string layout必须为 "2006-01-02 15:04:05"
    timeNowStr := timeNow.Format("2006-01-02 15:04:05")
    fmt.Println("tNow(string format): ", timeNowStr)

    timeNowUnixInt := timeNow.Unix()
    fmt.Println(timeNowUnixInt)

    // string转化为时间 layout必须为 "2006-01-02 15:04:05"
    t1, _ := time.Parse("2006-01-02 15:04:05", "2014-06-15 08:37:18")
    fmt.Println("t(time format): ", t1)
    t2 := time.Unix(1389058332, 0).Format("2006-01-02 15:04:05")
    fmt.Println("t(time format): ", t2)
}
阅读全文

Go 竞争检测


工作原理:
竞争检测器集成在 Go 工具链中。当使用了 -race 作为命令行参数后,编译器会插桩代码,使得所有代码在访问内存时,会记录访问时间和方法。同时运行时库会观察对共享变量的未同步访问。当这种竞争行为被检测到,就会输出一个警告信息。由于设计原因,竞争检测器只有在被运行的代码触发时,才能检测到竞争条件,因此在现实的负载条件下运行是非常重要的。但是由于代码插桩,程序会使用10倍的 CPU 和内存,所以总是运行插桩后的程序是不现实的。矛盾的解决方法之一就是使用插桩后的程序来运行测试。负载测试和集成测试是好的候选,因为它们倾向于检验代码的并发部分。另外的方法是将单个插桩后的程序布置到运行服务器组成的池中,并且给予生产环境的负载。

// 测试包
go test -race mypkg
// 编译和运行程序
go run -race mysrc.go
// 构建程序
go build -race mycmd
// 安装程序
go install -race mypkg
阅读全文

Golang 代码规范


项目目录结构规范
PROJECT_NAME
├── README.md 介绍软件及文档入口
├── bin 编译好的二进制文件
├── doc 该项目的文档
└── src 该项目的源代码

├── main 项目主函数
├── controller 路由控制
├── module 模块
└── conf 配置文件

包名
包名用小写,使用短命名,尽量和标准库不要冲突。

接口名
单个函数的接口名以”er”作为后缀,如 Reader、Writer

接口的实现则去掉“er”

type Reader interface {
    Read(p []byte) (n int, err error)
}







阅读全文

Go 文件操作


package main

import (
    "fmt"
    "os"
    "strconv"
)

func main() {
    // 打开文件 返回文件指针
    file, error := os.Open("/tmp/1.txt")
    if error != nil {
        fmt.Println(error)
    }
    fmt.Println(file)
    file.Close()

    // 以读写方式打开文件 返回文件指针 如果不存在则创建
    file2, error := os.OpenFile("/tmp/2.txt", os.O_RDWR|os.O_CREATE, 0644)
    if error != nil {
        fmt.Println(error)
    }
    fmt.Println(file2)
    file2.Close()

    // 创建文件 Create 函数也是调用的 OpenFile
    file3, error := os.Create("/tmp/3.txt")
    if error != nil {
        fmt.Println(error)
    }
    fmt.Println(file3)
    file3.Close()

    // 读取文件内容
    file4, error := os.Open("/tmp/1.txt")
    if error != nil {
        fmt.Println(error)
    }
    // 创建 byte 的 slice 用于接收文件读取数据
    buf := make([]byte, 1024)
    // 循环读取
    for {
        // Read 函数会改变文件当前偏移量
        len, _ := file4.Read(buf)
        // 读取字节数为 0 时跳出循环
        if len == 0 {
            break
        }
        fmt.Println(string(buf))
    }
    file4.Close()

    // 读取文件内容
    file5, error := os.Open("/tmp/1.txt")
    if error != nil {
        fmt.Println(error)
    }
    buf2 := make([]byte, 1024)
    ix := 0
    for {
        // ReadAt 从指定的偏移量开始读取,不会改变文件偏移量
        len, _ := file5.ReadAt(buf2, int64(ix))
        ix = ix + len
        if len == 0 {
        break
        }
        fmt.Println(string(buf2))
    }
    file5.Close()

    // 写入文件
    file6, error := os.Create("/tmp/4.txt")
    if error != nil {
        fmt.Println(error)
    }
    data := "This is data\r\n"
    for i := 0; i < 10; i++ {
        // 写入 byte 的 slice 数据
        file6.Write([]byte(data))
        // 写入字符串
        file6.WriteString(data)
    }
    file6.Close()

    // 写入文件
    file7, error := os.Create("/tmp/5.txt")
    if error != nil {
        fmt.Println(error)
    }
    for i := 0; i < 10; i++ {
        // 按指定偏移量写入数据
        ix := i * 64
        file7.WriteAt([]byte("This is data"+strconv.Itoa(i)+"\r\n"), int64(ix))
    }
    file7.Close()

    // 删除文件
    del := os.Remove("/tmp/1.txt")
    if del != nil {
        fmt.Println(del)
    }

    // 删除指定 path 下的所有文件
    delDir := os.RemoveAll("/tmp/testdir")
    if delDir != nil {
            fmt.Println(delDir)
    }
}
阅读全文

Go delete map


清空 map

import (
    "fmt"
    "time"
)

func main() {
    testMap := make(map[string]string)
    testMap["k"] = "v"
    fmt.Println(testMap)
    time.Sleep(time.Second * 2)
    for key, value := range testMap {
        fmt.Println(value)
        delete(testMap, key)
    }
    fmt.Println(testMap)
}

阅读全文

Go map使用


先声明 map

var m1 map[string]string

再使用 make 函数创建一个非 nil 的 map,nil map 不能赋值

m1 = make(map[string]string)

最后给已声明的 map 赋值

m1["a"] = "aa"
m1["b"] = "bb"

阅读全文