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 实现简单的 Queue 队列


需求
队列的特性较为单一,基本操作即初始化、获取大小、添加元素、移除元素等。最重要的特性就是满足先进先出。

实现
接下来还是按照以前的套路,一步一步来分析如何利用Go的语法特性实现Queue这种数据结构。

定义
首先定义每个节点 Node 结构体,照例 Value 的值类型可以是任意类型,节点的前后指针域指针类型为 Node

type node struct {
    value interface{}
    prev *node
    next *node
}

继续定义链表结构,定义出头结点和尾节点的指针,同时定义队列大小 size:

type LinkedQueue struct {
    head *node
    tail *node
    size int
}



阅读全文

Go convert "type []string" to string


package main

import (
    "fmt"
    "strings"
)

var naiveList string

func main() {
    naiveArray := []string{"a", "b", "c", "d", "e"}
    for _, v := range naiveArray {
        if naiveList == "" {
        naiveList = v
        } else {
        naiveList = strings.Join([]string{naiveList, v}, ",")
        }
    }
    fmt.Println(naiveList)
}
阅读全文

Go 字符串删除


删除在 s 字符串的头部和尾部中由 cutset 指定的字符, 并返回删除后的字符串

func Trim(s string, cutset string) string

删除首部和尾部的 ! 和空格

// "Achtung! Achtung"
fmt.Printf("%q\n", strings.Trim(" !!! Achtung! Achtung! !!! ", "! "))
// "a lone gopher"
fmt.Printf("%q\n", strings.TrimSpace(" \t\n a lone gopher \n\t\r\n"))
阅读全文

Go 字符串大小写替换


给定字符串转换为英文标题的首字母大写的格式 不能正确处理 unicode 标点

func Title(s string) string

所有字母转换为小写

func ToLower(s string) string

所有字母转换为大写

func ToUpper(s string) string

// Her Royal Highness
fmt.Println(strings.Title("her royal highness"))
// gopher123
fmt.Println(strings.ToLower("Gopher123"))
// GOPHER
fmt.Println(strings.ToUpper("Gopher"))
阅读全文

Go 字符串拼接


1.直接使用运算符

func BenchmarkAddStringWithOperator(b *testing.B) {
    hello := "hello"
    world := "world"
    for i := 0; i < b.N; i++ {
        _ = hello + "," + world
    }
}

golang 里面的字符串都是不可变的,每次运算都会产生一个新的字符串,所以会产生很多临时的无用的字符串,不仅没有用,还会给 gc 带来额外的负担,所以性能比较差。

2.fmt.Sprintf()

func BenchmarkAddStringWithSprintf(b *testing.B) {
    hello := "hello"
    world := "world"
    for i := 0; i < b.N; i++ {
        _ = fmt.Sprintf("%s,%s", hello, world)
    }
}

内部使用 []byte 实现,不像直接运算符这种会产生很多临时的字符串,但是内部的逻辑比较复杂,有很多额外的判断,还用到了 interface,所以性能也不是很。。

阅读全文

Go 处理 HTTP 请求


引入包

import (
    "net/http"
)

具体实现

// HTTPGet compatible http & https
func HTTPGet(reqURL string) []byte {
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    c := &http.Client{
        Transport: tr,
        Timeout:   30 * time.Second,
    }
    res, err := c.Get(reqURL)
    if err != nil {
        fmt.Println(err)
    }
    body, _ := ioutil.ReadAll(res.Body)
    res.Body.Close()
    responeDate := make(map[string]interface{})
    json.Unmarshal([]byte(string(body)), &responeDate)
    return responeDate
}

// HTTPPost is post func
func HTTPPost(reqURL, reqData string) map[string]interface{} {
    req, _ := http.NewRequest("POST", reqURL, strings.NewReader(reqData))
    req.Header.Set("Content-Type", "application/json")
    c := &http.Client{
        Timeout: 9 * time.Second,
    }
    res, err := c.Do(req)
    if err != nil {
        fmt.Println(err)
        return nil
    }
    body, _ := ioutil.ReadAll(res.Body)
    res.Body.Close()
    responeDate := make(map[string]interface{})
    json.Unmarshal([]byte(string(body)), &responeDate)
    return responeDate
}

// HTTPPut is post func
func HTTPPut(reqURL, reqData string) map[string]interface{} {
    req, _ := http.NewRequest("PUT", reqURL, strings.NewReader(reqData))
    req.Header.Set("Content-Type", "application/json")
    c := &http.Client{
        Timeout: 9 * time.Second,
    }
    res, err := c.Do(req)
    if err != nil {
        fmt.Println(err)
        return nil
    }
    body, _ := ioutil.ReadAll(res.Body)
    res.Body.Close()
    responeDate := make(map[string]interface{})
    json.Unmarshal([]byte(string(body)), &responeDate)
    return responeDate
}

// HTTPDelete is delete func
func HTTPDelete(reqURL, reqData string) map[string]interface{} {
    req, _ := http.NewRequest("DELETE", reqURL, strings.NewReader(reqData))
    req.Header.Set("Content-Type", "application/json")
    c := &http.Client{
        Timeout: 9 * time.Second,
    }
    res, err := c.Do(req)
    if err != nil {
        fmt.Println(err)
        return nil
    }
    body, _ := ioutil.ReadAll(res.Body)
    res.Body.Close()
    responeDate := make(map[string]interface{})
    json.Unmarshal([]byte(string(body)), &responeDate)
    return responeDate
}
阅读全文

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
阅读全文