美文网首页
Golang Snippets

Golang Snippets

作者: raku | 来源:发表于2018-12-12 14:13 被阅读0次

    Golang

    install

    Getting Started - The Go Programming Language

    首先下载得到 go1.11.2.linux-a md64.tar.gz

    tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz
    

    之后在 /usr/local 有个 go1.11.2 的目录

    然后加入 path

    export PATH=$PATH:/usr/local/goxxx/bin
    export GOROOT=/usr/local/goxxx
    

    Go都是值传递 不用指针函数中得到的就是一个copy

    func(f F) Foo {
    f.xx ...  //调用aaa.Foo函数时会产生aaa的一个copy
    }
    
    func(f *F) Foo {}// 这样就不会
    
    

    Array 是值传递 但是map slice channel 表现的很像是引用

    First, and most important, does the method need to modify the receiver? If it does, the receiver must be a pointer. (Slices and maps act as references)

    https://golang.org/doc/faq#references

    Error

    实现了Error方法的struct 都是error的实现 很方便的用于定制error类型

    type error interface {
        Error() string
    }
    
    
    

    JSON

    map to string

        fmt.Println("fill map::", rsFillmap)
        if jsonByte, err := json.Marshal(rsElemap); err != nil {
            fmt.Println(err)
        } else {
            fmt.Println("..............", string(jsonByte))
        }
    

    string to map

        jsonStr := `
        {
            "name":"liangyongxing",
            "age":12
        }
        `
        var mapResult map[string]interface{}
        if err := json.Unmarshal([]byte(jsonStr), &mapResult); err != nil {
            t.Fatal(err)
        }
    

    Slice

    仅声明 需要用make 但是初始化不用 所以一般用最后一种

    arr := make([]int, 5)
    
    // 实际上这里是一个slice
    args := []string{"what", "ever", "you", "like"}
    
    
    b := []string{}  //这样也可以创建一个slice  没有必要make
    
    

    关于interface

        mmm := map[string]interface{}{}
        data := `{"key": [{"haha": {"xixi": 10}}]}`
        json.Unmarshal([]byte(data), &mmm)
    
        if rs, ok := mmm["key"].([]map[string]interface{}); ok {
            fmt.Println("ok", rs)
        }else{
            fmt.Println("not ok")
        }
        //not ok
    
    
        if rs, ok := mmm["key"].([]interface{}); ok {
            fmt.Println("ok", rs)
            if rs2, ok2 := rs[0].(map[string]interface{}); ok2 {
                fmt.Println("ok2", rs2)
            }
    
        }else{
            fmt.Println("not ok")
        }   
      //ok2 map[haha:map[xixi:10]]
      //ok [map[haha:map[xixi:10]]]
    
    

    第一个if 结果是not ok 原因是go只知道key下面是一个数组 起元素都是interface{} 类型 不能知道这里的元素是 map[string]interface{}
    所以这么断言是not ok

    map init

    仅声明不赋值 需要用make
    初始化的同时赋值 可以用后一种

        m := make(map[string]int)
        m["haha"] = 1
        
        mm := map[string]int{
            "Bell Labs": 1,
            "MMM":2,
        }
        fmt.Printf("%+v  %+v", m, mm)
    
    
    

    Map range / Map iterate

    
    for k, v := range m {
        fmt.Printf("k=%v, v=%v\n", k, v)
    }
    
    
    删除某个key
    
    var sessions = map[string] chan int{};
    delete(sessions, "moo");
    
    

    map to struct

    go - Converting map to struct - Stack Overflow

    import "github.com/mitchellh/mapstructure"
    
    mapstructure.Decode(myData, &result)
    
    

    map key exists

    if _, ok := map[key]; ok {
       //存在
    }
    
    

    map & mutex

    Map不是线程安全的 当有多个资源同时写入&读取就会有问题,
    此时往往要用到锁 mutex
    这意味着线程将有序的对同一变量进行访问

    Exception Handing

    func Log(t interface{}) {
        defer func() {
            if p := recover(); p != nil {
                fmt.Printf("panic recover !!! p: %v", p)
                debug.PrintStack()
            }
        }()
    
        s := reflect.ValueOf(&t).Elem()
        typeOfT := s.Type()
    
        for i := 0; i < s.NumField(); i++ {
            f := s.Field(i)
            fmt.Printf("%d: %s %s = %v\n", i,
                typeOfT.Field(i).Name, f.Type(), f.Interface())
        }
    }
    
    
    

    String to Byte (byte to string)

    []byte(str)
    
    string(bytes)
    

    String To Int/Float

    
    #string to int  
    int,err:=strconv.Atoi(string)  
    #string to int64  
    int64, err := strconv.ParseInt(string, 10, 64)  
    
    #int to string  
    string:=strconv.Itoa(int)  
    #int64 to string  
    string:=strconv.FormatInt(int64,10)
    还可以使用 Sprint 更通用 对于 float 也可以处理
    fmt.Sprint(5.03)
    
    
    #string到 float
    iiii, _ := strconv.ParseFloat("32.23", 64)
    
    float to int
    int(float)
    
    int to float
    float(int)  float64(xxx)
    

    []int to string

    strings.Join 只能接收 []string

    strings.Trim(strings.Replace(fmt.Sprint(a), " ", delim, -1), "[]")
    
    

    :=

    := 左边有新变量即可

    #string到int  
    int,err:=strconv.Atoi(string)  
    #string到int64  
    int64, err := strconv.ParseInt(string, 10, 64)  
    #int到string  
    string:=strconv.Itoa(int)  
    #int64到string  
    string:=strconv.FormatInt(int64,10)  
    

    swap

    nums[I], nums[j] = nums[j], nums[I]

    defer

    PS 即使return defer 也会执行

    func f() (result int) {
    
      defer func() {
        result++
      }()
      return 0
    }
    上面函数返回1,因为defer中添加了一个函数,在函数返回前改变了命名返回值的值。是不是很好用呢。但是,要注意的是,如果我们的defer语句没有执行,那么defer的函数就不会添加,如果把上面的程序改成这样:
    
    func f() (result int) {
    
      return 0
      defer func() {
        result++
      }()
      return 0
    }
    上面的函数就返回0了,
    
    

    if else 作用域

    if 中声明的变量 在同一层else中也有效

    
        if bytes, err := json.Marshal(bizResp); err != nil {
            errMsg := fmt.Sprintf("fail to encode biz-resp : %v", bizResp)
            log.Error(errMsg)
            return nil, errors.New(errMsg)
        } else {
            refType := reflect.TypeOf(bizResp)
            w := &clueThrift.ThriftProtocolResponseWrapper{
                BaseResp:       &base.BaseResp{},
                JsonData:       string(bytes),
                OriginTypeName: refType.PkgPath() + "/" + refType.Name(),
            }
            return w, nil
        }
    

    []interface 数组赋值传参

    Frequently Asked Questions (FAQ) - The Go Programming Language

    当我们把一个string的slice 试图赋值给 一个interface的slice的时候
    cannot use arr (type []string) as type []interface {}

        sss := []string{"xxx"}
        AA(sss)
    
    
    func A(pp []interface{}){
        fmt.Println(pp)
    }
    
    

    可以这么做 (在作为参数传递时尤其如此) (PS 这是比较trick的做法)

      dest := []interface{}{}
        queries :=[]interface{}{}
        queries = append(queries, instanaceIds)
        dest = queries
    
    

    通过反射 调用interface{} 函数

    如果一个函数装进了interface{} 中,如何通过反射调用他呢?
    如何裸写一个goroutine pool | Legendtkl

    type worker struct {
        Func interface{}
        Args []reflect.Value
    }
    
    
            wk := worker{
                Func: func(x, y int) {
                    fmt.Println(x + y)
                },
                Args: []reflect.Value{reflect.ValueOf(i), reflect.ValueOf(i)},
            }
    
    
    reflect.ValueOf(ch.Func).Call(ch.Args)
    
    
    

    断言

    v := varI.(T)    //T是你要将接口转换的类型   // unchecked type assertion
    varI 必须是一个接口变量,否则编译器会报错:
    
    

    再看一个断言的例子

    package main
    
    import "fmt"
    import "reflect"
    
    type Xixi struct{
        A string
    }
    
    
    func main() {
        test(Xixi{A:"aaa"})
        testSlice([]Xixi{Xixi{A:"aaa"}, Xixi{A:"abb"}, Xixi{A:"acc"}})
    }
    
    func test(any interface{}){
        v := reflect.ValueOf(any)
        fmt.Printf("%+v  %+v\n", v, any)  //{A:aaa}  {A:aaa} //但是此时并不能 v.A  //因为go并不知道这究竟是哪种类型的数据
        if realV, ok := any.(Xixi); ok {
            fmt.Printf("%+v %+v ...\n", realV, realV.A)
        }
    } 
    
    func testSlice(any interface{}){
        v := reflect.ValueOf(any) //可以将any识别出是 [] //此时就可以循环了
        for i := 0; i < v.Len(); i++ {
            fmt.Printf("%+v\n", v.Index(i))
        }   
        
        //当然可以用断言一步到位
        if realV, ok := any.([]Xixi); ok{
            fmt.Println(len(realV), realV[0]) //3 {aaa}
        }else{
            fmt.Println("err")
        }
    }
    
    

    得到变量类型

    reflect.TypeOf(x)
    
    
        b := "123"
        fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b).Kind())
      string string
    
    
    
    var x float64 = 3.4
    fmt.Println("value:", reflect.ValueOf(x).String())
    

    将interface{} 还原成原来实参(reflect)

    Range a interface that holds a slice (or map)

    当一个interface{} 里实际存着是一个slice的时候 如何range这个interface{} 呢
    这里不是最好
    的办法 最好的方式是用断言

    reflect.TypeOf(t) 返回Person (也就是struct的名字 )
    reflect.TypeOf(t).Kind() 返回 struct 也就是更基本的类型

    package main
    
    import "fmt"
    import "reflect"
    
    func main() {
        data := []string{"one","two","three"}
        test(data)
        moredata := []int{1,2,3}
        test(moredata)
        ddd := make(map[int]string)
        ddd[1]= "xixi"
        test(ddd)
    } 
    
    func test(t interface{}) {
      switch reflect.TypeOf(t).Kind() {
        case reflect.Slice:
            s := reflect.ValueOf(t)
    
            for i := 0; i < s.Len(); i++ {
                fmt.Println(s.Index(i))
            }
          case reflect.Map:
            v := reflect.ValueOf(t)
            for _, key := range v.MapKeys() {
                strct := v.MapIndex(key)
                fmt.Println(key.Interface(), strct.Interface())
            }
        }
    }
    
    
    

    << && >>

    
    n << x  表示 n * (2^x). 
    y >> z  表示 y / (2^z).
    
    

    枚举类型实现

    
    type ModelField int8
    
    const (
        NULL          ModelField = iota
        AdvId
        InstanceId
        Name
        ComponentType
        CreateTime
        Status
        IsDel
        LotteryPool
        // ......
    )
    
    fmt.Print("id::", InstanceId, AdvId)  //得到 2 和 1
    

    当然了 ModelField = iota 也可以替换成 int = itoa
    这样写成 type 有个好处就是可以为这个类型增加一个方法

    作用域

    如果一个type 定义在func 中 那么只有这个func 才能使用这个type 外面并不能访问

    打印指针

        a1 := AA{}
        a2 := AA{}
        a1.A = A{Ha:"xx"}
        fmt.Printf("... %p ... %p", &a1, &a2)
    

    修改map中的值

    map里是struct等复杂的对象 是不可以被直接修改的
    比如
    map[key] = A{}
    然后又想 map[key].xx = xx
    这样不行哦

    很多时候要把map中的元素传到另一个func 中去修改 那么就要传指针 然而

    https://github.com/golang/go/issues/11865
    spec: can take the address of map[x]
    也就是说不能够 &map[key]

    那怎么办? 干脆在创建map的时候就用value的指针 而不是 value

        s := make(map[string]*Student)
        s["chenchao"] = &Student{
            Name:"chenchao",
            Id:111,
        }
        s["chenchao"].Id = 222
    
    

    为基础类型增加方法(自定义 Int)

    
    type Int int
    
    func (i Int) Add(j Int) Int {
      return i + j
    }
    
    func main() {
      i := Int(5)
      j := Int(6)
      fmt.Println(i.Add(j))
      fmt.Println(i.Add(j) + 12)
    }
    
    
    

    不定长参数

    函数接收一个不定长参数
    和ES6有些不同的是
    golang中
    …XXX 是将多个item 合并到一个[]
    XXX… 是打散[]

    但是ES6中 形参 …xxx 是将多个item合并到xxx 比如function a(…xxx){}
    如果 …xxx 这样的写法作为实参 就是打散 a(…xxx)

    type Xixi struct{
        A string
    }
    
    
    func main() {
        f := func() interface{} {
            return 1
        }
        
        
        test(1, "wowo", Xixi{A: "aia"}, f)
    }
    
    func test(haha ...interface{}) {
        //haha 是一个[]  数组OR slice
        fmt.Println(reflect.TypeOf(haha)) //[]interface {}  
        fmt.Printf("%+v \n", haha) //[1 wowo 0x108f470] 
        fmt.Printf("%+v\n", reflect.ValueOf(haha[2])) //{A:aia}
        test2(haha) //得到 1  注意这样传下去 是把整个[]传到下一个函数了 
        test2(haha...) //把[]打散作为多个实参 得到4  //注意这里和ES6语法上的不同
    }
    func test2(xixi ...interface{}){
        fmt.Printf("%+v\n", len(xixi))
    }
    

    注意和ES6的区别

    var s = (...haha) => {
        console.log(haha)
        arr = []
        arr = arr.concat(...haha)
        console.log(arr)
        s2(...haha)
    }
    
    var s2 = (...xixi) => {
        console.log(xixi)
    }
    
    s(1,2,3)
    
    //=======================================
    var xxx = [1,2,3,4]
    function vvv(a,b,c,d){
    console.log(a,b,c,d)
    }
    vvv(...xxx) //打散
    
    
    

    组合

    组合有一点点像继承,可以直接访问到父类的成员变量

    type Proto struct {
        BaseField string
    }
    
    type Zero struct {
        Proto
        ZeroFiled string
    }
    
    func main() {
        zero := Zero{
            Proto: Proto{
                BaseField: "1",
            },
            ZeroFiled: "sd",
        }
        fmt.Printf("%+v  %+v", zero.BaseField, zero.Proto.BaseField)
    }
    
    

    interface 多态

    golang实际上是通过组合实现的继承
    Golang中的面向对象继承

    struct 可以嵌套 struct
    struct 可以嵌套 interface{}
    interface 也可以嵌套 interface{}

    interface 继承 method-has-a-pointer-receiver 问题

    
    type Pet interface {
        SetName() string
    }
    type Dog struct {
        Name string
    }
    
    func (g *Dog) SetName() string {
        g.Name = "..."
        return "..."
    }
    
    
    func Test_inter(t *testing.T) {
        var p Pet
        g := Dog{}
        p = g  //这里会报错 method has a pointer receiver
      p = &g //这是最简单的一种解决方案 
        p.SetName()
        fmt.Printf(g.Name)
    }
    
     
    
    

    参考 go - X does not implement Y (… method has a pointer receiver) - Stack Overflow
    第一个回答还提到了另一个解决方案. 用另外一个结构体包装一下
    用结构体包装一下 实际上还是借用指针

    import "fmt"
    
    type Stringer interface {
        String() string
    }
    
    type MyType struct {
        value string
    }
    
    func (m *MyType) String() string { return m.value }
    
    type MyType2 struct {
        *MyType
    }
    
    func main() {
        var s Stringer
        m := MyType{value: "something"}
        // s = m // has a pointer receiver error
        m2 := MyType2{MyType: &m}
        s = m2
        fmt.Printf(s.String())
    }
    
    

    go testing!!

    首先待测文件命名要是 xxx_test.go
    需要进入到待测文件所在目录
    运行某个待测文件

    go test -v xxx_test.go
    

    -v 表示显示log

    运行某一个方法

    go test -count=1 -v -run Test_service_ListSites$  (Test_service_ListSites 是待测的方法的regexp表达式)
    
    package listing
    
    import (
        "testing"
    
        "github.com/stretchr/testify/assert"
    )
    
    func Test_service_ListSites(t *testing.T) {
        assert := assert.New(t)
        assert.True(true)
    }
    
    

    for range

      第一个参数是idx
        for _, p := range lotterInstanceData.Prize{
            p.AdvId = advId
        }
    
    

    error and panic

    在go里面 错误和异常是不同的
    错误是自己手动创建出来的一个类型 异常就像其他语言需要try起来的部分

    
    func funcA() error {
        defer func() {
            if p := recover(); p != nil {
                fmt.Printf("panic recover! p: %v\n", p)
                
            }
        }()
        return funcB()
    }
    
    func funcB() error {
        // simulation
        // panic("foo")
        return errors.New("!funb erroR!")
    }
    
    

    Err to string

    err.Error() 
    
    func test() {
        err := funcA()
        if err == nil {
            fmt.Printf("test err is nil\\n")
        } else {
            fmt.Printf("test err is %v\\n", err)
        }
    }
    
    
    func main() {
        test()
    }
    
    

    json to map

    json to map 之后 数值类型都是float64

        mmm := map[string]interface{}{}
        data := `{"key": 10}`
        json.Unmarshal([]byte(data), &mmm)
        fmt.Printf("\n %+v \n", reflect.TypeOf(mmm["key"]))
      //float64
    
    

    struct简写 property

    type server struct {
        *app.App
    }
    相当于
    type server struct {
        App *app.App
    }
    

    在同一个目录里有多个main函数

    文件开头加上 // +build OMIT

    dlv build

    go build -o ./cmd/insight/dlv -gcflags "all=-N -l" ./cmd/insight/main.go

    然后
    dlv --listen=:2345 --headless=true --api-version=2 exec ./dlv
    此时会等待 goland 的debug连接

    点开虫子图标,就启动辣

    
    **#!/usr/bin/env bash**
    CURDIR=**$***(pwd)*
    *echo*$CURDIR
    *rm*./cmd/meteor-api/dlv
    *go*build -o ./cmd/meteor-api/dlv -gcflags "all=-N -l" ./cmd/meteor-api/main.go
    *cd***$**{CURDIR}/cmd/meteor-api/
    *dlv*--listen=:2345 --headless=true --api-version=2 exec ./dlv
    
    
    

    time format 当前时间

    fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
    
    // Y. M .D
    thisMonth.AddDate(0, 0, -1).Format(DATE_FORMAT)
    
    theTime.Unix() //转化为时间戳 类型是int64
    
    // 生成时间对象
    startTime, err = time.Parse("2006-01-02T15:04:05.000Z", "2019-01-18T16:00:00.000Z")
    
       //时间戳 to 时间
       tm := time.Unix(1531293019, 0)
       fmt.Println(tm.Format("2006-01-02 15:04:05")) //2018-07-11 15:10:19
    
    

    go mod 1.11

    Modules · golang/go Wiki · GitHub

    默认 GO111MODULE 的值是 auto

    如果你的项目在 go path 下 但是仍然希望使用新的包管理
    需要设置

    set -x GO111MODULE on
    

    需要go mod init YOUR_MOD_NAME 新建一个go.mod

    go build go get go run 等 go 命令会更新 go.mod 文件 (也是在GO111MODULE on 的情况下)

    GO111MODULE on 的情况下才能使用 go mod vendor

    dep

    如果是 go1.11之前的 推荐使用 dep 包管理

    
    dep ensure -add xxx@master
    

    rand

    (100) //产生0-100的随机整数

    匿名字段

    type Human struct {
        name string
        age int
        weight int
    }
    
     
    
    type Student struct {
        Human  // 匿名字段,那么默认Student就包含了Human的所有字段
        speciality string
    }
    
    

    匿名结构体

    json.Marshal(struct{
        Name string
        age int
    }{"name",18})
    
    func (t AAA) GetType() struct {
        IsSDK bool
    } {
        return struct{
            IsSDK bool
        }{
            IsSDK: false,
        }
    
    }
    
    
    

    litter 方便好用的 print

    import "github.com/sanity-io/litter"
    
    var (
        Dump  = litter.Dump
        Sdump = litter.Sdump
    )
    
    

    函数是一等公民

    type A struct {
        Count func(c int) int
    }
    // 这样是表示在 A 类型中有一个 property Count 它是一个函数
    // 然后这样赋值
    c := A{
        Count: func(c int) int { return 12 },
    }
    
    
    

    嵌套结构体初始化

    type Account struct {
        Id     uint32
        Name   string
        Nested struct {
            Age uint8
        }
    }
    
    //方法1  不推荐  太麻烦  而且容易出错
    account := &Account{
            Id:   10,
            Name: "jim",
            Nested: struct {
                Age uint8
            }{
                Age: 20,
            },
        }
    
    //方法2  推荐
    acc ;= new Account()
    acc.Nested.Age = 20
    OR
    acc := Account{}
    acc.Nested.Age = 29
    
    
    

    enum

    go 没有枚举关键字 但是可以通过 const + itoa 来实现
    itoa + 1 表示从1 开始

        type State int
        const (
            Phone State = iota + 1
            Form
            MapSearch
        )
    
      const (
          Haha int = 5
      )
    
    
    

    switch

    
        type State int
        const (
            Phone State = iota + 1
            Form
            MapSearch
        )
    
        day := State(1)
        
        switch day {
        case Phone:
            fmt.Print(day)
        default:
            fmt.Print(0)
        }
    
    
        str :="5"
        switch str {
            case "5","3":   
                fmt.Print("hahah in!")
            case "2":
                fmt.Print("hahah 222!")     
        }
    

    获取当前程序所在目录

    func getCurrentFilePath() string {
    
        dir, err := os.Getwd()
        if err != nil {
            logs.Debug("current file path err %+v", err)
        }
        fmt.Printf("current dir : %+v", dir)
    
        return dir
    }
    
    
    

    channel

    go语言之行—golang核武器goroutine调度原理、channel详解 - W-D - 博客园
    Goroutine本质上是协程,可以理解为不受内核调度,而受go调度器管理的线程。
    协程与线程主要区别是它将不再被内核调度,而是交给了程序自己而线程是将自己交给内核调度,所以也不难理解golang中调度器的存在

    golang协程——通道channel阻塞 - Go语言中文网 - Golang中文社区
    并发的存在,就涉及到线程通信。在当下的开发语言中,线程通讯主要有两种,共享内存与消息传递。
    golang对并发的处理采用了协程的技术。golang的goroutine就是协程的实现。协程的概念很早就有,简单的理解为轻量级线程。
    goroutine就是为了解决并发任务间的通信而设计的
    golang解决方案是消息传递机制,消息的传递通过channel来实现

    CSP : Communicating Sequential Process 的简称, 是一种并发编程模型,由 Tony Hoare 于 1977 年提出

    深入理解 Go Channel | Legendtkl

    Example1

    Channel 一般是用在协程之间OR和主线程通信用的 一般不会再同一个线程中写入和读取,这么做会有 dead lock 报错
    如果一定要这么做,那么这个channel 必须有缓冲区

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func writeMessages(messages chan string) {
        time.Sleep(1000 * time.Millisecond)
        messages <- "ping"
    }
    func main() {
    
        messages := make(chan string)
    
        //如果整个程序中没有给这个 channel 写入值 会有 dead lock 报错
      //因为从逻辑上说会永远卡在<-messages
        go writeMessages(messages)
        
        rs := <-messages
        fmt.Printf("rs  %+v", rs)
    
      close(messages)
        fmt.Println("just end")
    }
    
    
    

    Example2

    写入和读取发生在同一个线程OR协程,必须带有缓冲区的 channel

    messages := make(*chan*string, 2)
    messages <- "buffered1"
    
    fmt.Println(<-messages)
    messages <- "buffered2"
    fmt.Println(<-messages)
    messages <- "buffered3"
    messages <- "buffered4"
    fmt.Println(<-messages)
    fmt.Println(<-messages)
    
    

    Example 3 range

    package main                                                                                             
    import (
        "fmt"
        "time"
        "strconv"
    )
    
    func makeCakeAndSend(cs chan string, count int) {
        for i := 1; i <= count; i++ {
            cakeName := "Strawberry Cake " + strconv.Itoa(i)
            time.Sleep(1 * time.Second)
            cs <- cakeName //send a strawberry cake
        }   
    }
    
    func receiveCakeAndPack(cs chan string) {
        for s := range cs {
            fmt.Println("Packing received cake: ", s)
        }
    }
    
    func main() {
        cs := make(chan string)
        go makeCakeAndSend(cs, 5)
        go receiveCakeAndPack(cs)
    
        //sleep for a while so that the program doesn’t exit immediately
        time.Sleep(13 * 1e9)
    }
    
    

    channel with direction

    When using channels as function parameters, you can specify if a channel is meant to only send or receive values. This specificity increases the type-safety of the program.

    package main
    import "fmt"
    func ping(pings chan<- string, msg string) {
        pings <- msg
    }
    func pong(pings <-chan string, pongs chan<- string) {
        msg := <-pings
        pongs <- msg
    }
    func main() {
        pings := make(chan string, 1)
        pongs := make(chan string, 1)
        ping(pings, "passed message")
        pong(pings, pongs)
        fmt.Println(<-pongs)
    }
    
    
    

    Select{}

    select关键字用于多个channel的结合

    package main
    
    import (  
        "fmt"
        "time"
    )
    
    func server1(ch chan string) {  
        time.Sleep(6 * time.Second)
        ch <- "from server1"
    }
    func server2(ch chan string) {  
        time.Sleep(3 * time.Second)
        ch <- "from server2"
    
    }
    func main() {  
        output1 := make(chan string)
        output2 := make(chan string)
        go server1(output1)
        go server2(output2)
    
          // 这里会阻塞 其中任何一个 channel 可以取出值就可以放行
        // 如果同时两个 channel 都满足 会随机选一个 channel
        // 当你把 server1时间也设置3 有时结果是2 有时是1
        select {
        case s1 := <-output1:
            fmt.Println(s1)
        case s2 := <-output2:
            fmt.Println(s2)
        }
          fmt.Println("just end")
    }
    
    

    关于select case 中的 default

        messages := make(chan string)
    
        select {
        case messages<-"箱":
            fmt.Printf("111")
        default:
            fmt.Printf("xxxx")
        }
    
    

    这里完全没有开启任何一个协程就在向这个 channel 写数据
    按理说会 dead lock 但是因为有 default 语句 虽然第一个 messages 的 case 无法执行 但是有 default 所以输出 xxx

    empty select

    Block forever

    
    package main
    
    import (
        "fmt"
        "time"
    )
    
    func backgroundTask() {
        ticker := time.NewTicker(1 * time.Second)
        for _ = range ticker.C {
            fmt.Println("Tock")
        }
    }
    
    func main() {
        fmt.Println("Go Tickers Tutorial")
    
        go backgroundTask()
    
        // This print statement will be executed before
        // the first `tock` prints in the console
        fmt.Println("The rest of my application can continue")
        // here we use an empty select{} in order to keep
        // our main function alive indefinitely as it would
        // complete before our backgroundTask has a chance
        // to execute if we didn't.
        select {}
    }
    
    
    
    

    Another example

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func server1(ch chan string) {
        for {
            time.Sleep(1 * time.Second)
            ch <- "from server1"
    
        }
    }
    
    
    func getVal(ch chan string) {
        for {
            fmt.Printf("  %v \n", <-ch)
        }
    }
    
    func main() {
        output1 := make(chan string)
        go server1(output1)
        go getVal(output1)
        select {}
    
        fmt.Println("just end")
    }
    
    
    

    Ticker Channel Coroutine 协程

    Go by Example: Timers and Tickers

    package main
    
    import "time"
    
    func main() {
        timer := time.NewTimer(time.Second * 2)
        <- timer.C  //will block until has value
        println("Timer expired")
    }
    
    
    

    Ticker Channel simulates setInterval

    package main
    
    import "time"
    import "fmt"
    var ticker *time.Ticker 
    func main() {
        ticker = time.NewTicker(time.Millisecond * 100)
        go func() {
            for {
                select {
                case rs,ok := <-ticker.C:
                    fmt.Println("Tick at",ok, rs)
                }
    
            }
        }()
        time.Sleep(time.Millisecond * 1500)
        ticker.Stop()
        fmt.Println("Ticker stopped")
    }
    
    
    
        timeChan := time.NewTimer(time.Second).C
        
        tickChan := time.NewTicker(time.Millisecond * 400).C
        
        doneChan := make(chan bool)
        go func() {
            time.Sleep(time.Second * 2)
            doneChan <- true
        }()
        
        for {
            select {
            case <- timeChan:
                fmt.Println("Timer expired")
            case <- tickChan:
                fmt.Println("Ticker ticked")
            case <- doneChan:
                fmt.Println("Done")
                return
          }
        }
    
    

    go func() 并行

    
    func main() {
        go func() {
            time.Sleep(1 * time.Second)
            fmt.Println("11111")
        }()
        go func() {
            time.Sleep(3 * time.Second)
            fmt.Println("3333")
        }()
    }
    
    // 注意这样并不能得到输出 因为主进程执行完了就退出了  那么两个协程也没了
    需要这样
    func main() {
        go func() {
            time.Sleep(1 * time.Second)
            fmt.Println("11111")
        }()
        go func() {
            time.Sleep(3 * time.Second)
            fmt.Println("3333")
        }()
        time.Sleep(5 * time.Second)
        fmt.Println("55555")
    }
    
    func UnblockGet(requestUrl string) chan string {
        resultChan := make(chan string)
        go func() {
            request := httplib.Get(requestUrl)
            content, err := request.String()
            if err != nil {
                content = "" + err.Error()
            }
            resultChan <- content
        } ()
        return resultChan
    }
    
    

    waitGroup

    Notice, the Add must go ahead of Done

    // This example fetches several URLs concurrently,
    
    // using a WaitGroup to block until all the fetches are complete.
    
    func ExampleWaitGroup() {
    
        var wg sync.WaitGroup
    
        var urls = []string{
    
            "http://www.golang.org/",
    
            "http://www.google.com/",
    
            "http://www.somestupidname.com/",
    
        }
    
        for _, url := range urls {
    
            // Increment the WaitGroup counter.
    
            wg.Add(1)
    
            // Launch a goroutine to fetch the URL.
    
            go func(url string) {
    
                // Decrement the counter when the goroutine completes.
    
                defer wg.Done()
    
                // Fetch the URL.
    
                http.Get(url)
    
            }(url)
    
        }
    
        // Wait for all HTTP fetches to complete.
    
        wg.Wait()
    
    }
    
    
    

    if 和 表达式

    
          u, err := url.Parse("https://siongui.github.io/pali-chanting/zh/archives.html")
          if err != nil {
                  log.Fatal(err)
          }
          parts := strings.Split(u.Hostname(), ".")
          domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
          fmt.Println(domain)
    
    

    用下面的方式变量 u 只能在 if 和 else if 中使用

    
    if u, err := url.Parse(urlstr); err != nil {
        logs.Error("url parse error ::: %+v url:::%+v", err, urlstr)
        *return*urlstr
    } *else*{
        parts := strings.Split(u.Hostname(), ".")
        domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
        *return*domain
    }
    
    
    
    

    go regexp

        pat := `(((abc.)def.)ghi)`
        src := `abc-def-ghi abc+def+ghi`
    
        fmt.Println(regexp.MatchString(pat, src))
        // true <nil>
    
        fmt.Println(regexp.QuoteMeta(pat))
    
    

    go slice join

    import strings
    stringFiles := strings.Join(fileSlice[:], ",")
    
    
    //Back to Slice again
    
    import strings
    fileSlice := strings.Split(stringFiles, ",")
    
    

    reverse slice

    实际生产环境可以使用linq 这个库

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    func reverse(s []interface{}) {
        for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
            s[i], s[j] = s[j], s[i]
        }
    }
    
    func reverseAny(s interface{}) {
        n := reflect.ValueOf(s).Len()
        swap := reflect.Swapper(s)
        for i, j := 0, n-1; i < j; i, j = i+1, j-1 {
            swap(i, j)
        }
    
    }
    
    func main() {
        s := []interface{}{1, "2", uint(3), byte(4), float64(5)}
        reverse(s)
        fmt.Println(s)
        reverseAny(s)
        fmt.Println(s)
    }
    
    
    

    还可以使用https://github.com/ahmetb/go-linq 这个库
    这个库似乎虽然是linq 但有一些slice的功能

    http req resp

    方法1 不推荐 resp.Body 只能被读一次

    resp, err := http.Post(url, “application/json”, bytes.NewBuffer([]byte(sql)))
    json.NewDecoder(resp.Body).Decode(&respData)
    

    方法2 ioUtil

    req, err := http.NewRequest("GET", url, nil)
    req.Header.Add("X-Orange-Caller", "ad.tetris.site_server")
    resp, err := client.Do(req)
    body, err := ioutil.ReadAll(resp.Body)
    defer resp.Body.Close()
    logs.Debug(string(body))
    
    

    buf

    resp, err := http.Post(url, "application/json", bytes.NewBuffer([]byte(sql)))
    
    buf := new(bytes.Buffer)
    buf.ReadFrom(resp.Body)
    s := buf.String()
    
    
    

    block goroutine & play as sleep

    https://blog.sgmansfield.com/2016/06/how-to-block-forever-in-go/
    Other way describe in the article needs go func(){….}

    <-time.After(time.Duration(math.MaxInt64))
    
    <-time.After(time.Duration(2 * time.Second))
    fmt.Printf("........")
    
    

    获取函数名

    pc, _, _, _ := runtime.Caller(1)
    
    // Retrieve a Function object this functions parent
    functionObject := runtime.FuncForPC(pc)
    
    // Regex to extract just the function name (and not the module path)
    extractFnName := regexp.MustCompile(`^.*\.(.*)$`)
    name := extractFnName.ReplaceAllString(functionObject.Name(), "$1")
    
    

    json unmarshal 字段类型不统一

    https://github.com/francoispqt/gojay#decoding

    package gojay
    
    import (
        "log"
        "reflect"
        "strconv"
        "testing"
    )
    
    type user struct {
        id    int
        name  string
        email string
    }
    
    // implement gojay.UnmarshalerJSONObject
    func (u *user) UnmarshalJSONObject(dec *Decoder, key string) error {
        switch key {
        case "id":
            var tmp interface{}
            var err error
            var intVal int
            err = dec.Interface(&tmp)
            if err != nil {
                return err
            }
            log.Printf(":::%+v", reflect.TypeOf(tmp).Kind())
            if reflect.TypeOf(tmp).Kind().String() == "string" {
                intVal, err = strconv.Atoi(tmp.(string))
                u.id = intVal
            } else if reflect.TypeOf(tmp).Kind().String() == "float64" {
                u.id = int(tmp.(float64))
            }
            return err
        case "name":
            return dec.String(&u.name)
        case "email":
            return dec.String(&u.email)
        }
        return nil
    }
    func (u *user) NKeys() int {
        return 3
    }
    func TestDecoderMe(t *testing.T) {
        u := &user{}
        d := []byte(`{"id":"1213","name":"gojay","email":"gojay@email.com"}`)
        err := UnmarshalJSONObject(d, u)
    
        if err != nil {
            log.Fatal(err)
        }
    }
    
    
    
    

    pprof

    Profiling Go Programs - The Go Blog

    先起一个9876端口的服务

      go func() {
            fmt.Println("pprof start...")
            fmt.Println(http.ListenAndServe(":9876", nil))
        }()
    
    

    http://x.x.x.x:9876/debug/pprof/ 会打开一个界面

    关于goroutine的话 可以借用这个来分析

    先
    go tool pprof http://x.x.x.x:9876/debug/pprof/goroutine
    再
    web
    之后会生成一个svg
    

    相关文章

      网友评论

          本文标题:Golang Snippets

          本文链接:https://www.haomeiwen.com/subject/lxouhqtx.html