Go 轻量级 MySQL 库 go-sql-driver


https://github.com/go-sql-driver/mysql

引入库

import (
    "database/sql"

    _ "github.com/go-sql-driver/mysql"
)

查询数据

// 建立 MySQL 链接
db, err := sql.Open("mysql", "user:passwd@tcp(ip:port)/db")
if err != nil {
    fmt.Println(err)
}
defer db.Close()
// 执行 SQL
rows, rerr := db.Query(sqlInfo)
if rerr != nil {
    fmt.Println(rerr)
}
defer rows.Close()
// 获取 SQL 数据
for rows.Next() {
    var returnData string
    err := rows.Scan(&returnData)
    if err != nil {
        fmt.Println(err)
    }
    return returnData
}
return returnData

写入/更新数据

// 建立 MySQL 链接
db, err := sql.Open("mysql", "user:passwd@tcp(ip:port)/db")
if err != nil {
    fmt.Println(err)
}
defer db.Close()
// 执行 SQL
db.Exec(sqlInfo)
阅读全文

Go 语言小细节(四)


map 的容量
你可以在 map 创建时指定它的容量,但你无法在 map 上使用 cap() 函数。

package main
func main() {  
    m := make(map[string]int,99)
    cap(m)
}

匿名函数作用域陷阱

import (
    "fmt"
)

func main(){
    var msgs []func()
    array := []string{
        "1", "2", "3", "4",
    }
    for _, e := range array{
        msgs = append(msgs, func(){
            fmt.Println(e)
        })
    }
    for _, v := range msgs{
        v()
    }
}

阅读全文

Go 语言小细节(三)


在 for 语句的闭包中使用迭代变量会有问题
在 for 迭代过程中,迭代变量会一直保留,只是每次迭代值不一样。因此在 for 循环中在闭包里直接引用迭代变量,在执行时直接取迭代变量的值,而不是闭包所在迭代的变量值。如果闭包要取所在迭代变量的值,就需要 for 中定义一个变量来保存所在迭代的值,或者通过闭包函数传参。

package main

import (  
    "fmt"
    "time"
)

func forState1(){
    data := []string{"one","two","three"}

    for _,v := range data {
        go func() {
            fmt.Println(v)
        }()
    }
    time.Sleep(3 * time.Second)
    // three, three, three

    for _,v := range data {
        vcopy := v
        // 使用临时变量
        go func() {
            fmt.Println(vcopy)
        }()
    }
    time.Sleep(3 * time.Second)
    // one, two, three

    for _,v := range data {
        go func(in string) {
            fmt.Println(in)
        }(v)
    }
    time.Sleep(3 * time.Second)
    // one, two, three
}

func main() {  
    forState1()
}

阅读全文

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)
}







阅读全文