安装多个版本的 go

GVM

Go依赖管理与 GOPATH 的关系.

GOPATH环境变量中可以包含多个目录。

go get

这种方式,它会将依赖下载在$GOPATH环境变量中的第一个目录中.

自己对Go中包的理解

编写代码时,使用的`package`,表示代码所在的包名。

引用时,`import package`,这个`package`表示相对于`GOPATH`中的`src`目录下的目录名,多层目录,使用`/`分隔。
一般来说,引用的`package`的名字,和你的最后一个目录的名字相同,但也可以不同。`import package`的名字,以所在包名的名字为准,而不是以目录名为准.

Golang中的字符串拼接

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer

    for i := 0; i < 1000; i++ {
        buffer.WriteString("a")
    }

    fmt.Println(buffer.String())
}

vscode中调试Go

can't load package: package .: no buildable Go source files in /ihome/go/golang-weibo-sdk
exit status 1

这个问题是由于你没有将设置指向main.go的所在目录里.即,在Go的debug设置里,要类似如下:

{
    "version": "0.2.0",
    "configurations": [{
        "name": "Launch",
        "type": "go",
        "request": "launch",
        "mode": "debug",
        "remotePath": "",
        "port": 2345,
        "host": "127.0.0.1",
        "program": "${workspaceRoot}/src/main",
        "env": {},
        "args": [],
        "showLog": true
    }]
}

即,program要设置为指向Go的main函数文件所在的目录即可.

Go单元测试

必须要将命令行,先切换到要进行单元测试文件的目录的根目录。比如:

$GOPATH/src/utils/myutil.go
$GOPATH/src/utils/myutil_test.go

要想进行 myutil_test.go 的单元测试,则先切换到目录$GOPATH/src/utils,然后在该目录下执行命令:go test即可。

单元测试某个指定的方法

go test -run mehtodName

单元测试递归执行

进入到$GOPATH的根目录,然后按以下方式执行即可:
go test com.github.emacsist.weibosdk/api...

关于单元测试中的 Log(), Logf()

默认的情况下,go test并不会输出这些, 如果需要的话,可以添加go test -v来输出详细信息.

Golang中类似Java中的toString()功能

package resp

import (
	"fmt"
)

// ErrorResp : 错误码返回对象
type ErrorResp struct {
	Request   string `json:"request"`
	ErrorCode int32  `json:"error_code"`
	Error     string `json:"error"`
}

func (errorResp *ErrorResp) String() string {
	return fmt.Sprintf("request=%v\nErrorCode=%v\nError=%v\n", errorResp.Request, errorResp.ErrorCode, errorResp.Error)
}

即为它添加一个方法 String() string即可,这样子,在调用fmt.Printf()这些格式化打印时,就会自动调用对象的String()方法来打印了.

int64 to String


import "strconv"

strconv.FormatInt(int64Value, 10)

通过反射遍历结构体

package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Age  int
	Name string
}

func main() {
	p := Person{Age: 10, Name: "HelloWorld"}

	refValue := reflect.ValueOf(&p)

	fields := refValue.Elem()
	for i := 0; i < fields.NumField(); i++ {
		field := fields.Field(i)
		fieldName := fields.Type().Field(i).Name
		fieldValue := field.Interface()
		fmt.Printf("fieldName = %v, fieldValue = %v\n", fieldName, fieldValue)
	}
}

要注意的是,fieldName是通过fields这个来获取的.

如果想要将字段的值转换为确定类型的值,则就可以这样子做:

package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Age  int
	Name string
}

func main() {
	p := Person{Age: 10, Name: "HelloWorld"}

	refValue := reflect.ValueOf(&p)

	fields := refValue.Elem()
	for i := 0; i < fields.NumField(); i++ {
		field := fields.Field(i)
		fieldName := fields.Type().Field(i).Name

		if field.Type().Name() == "string" {
			fieldValue := field.Interface().(string)
			fmt.Printf("string --> fieldName = %v, fieldValue = %v\n", fieldName, fieldValue)
		} else if field.Type().Name() == "int" {
			fieldValue := field.Interface().(int)
			fmt.Printf("int --> fieldName = %v, fieldValue = %v\n", fieldName, fieldValue)
		}

	}
}

将json字段串自动转化生成struct结构

mervine.net mholt.github.io

json 字符串 转换为 struct

import "encoding/json"

json.Unmarshal([]byte(body), &status)

struct 对象 转换为json字符串

p为结构体变量
body, error := json.Marshal(p)

示例

package main

import (
	"encoding/json"
	"fmt"
)

// Person : test object
type Person struct {
	Name string
	Age  int
}

func changeValue(p Person) {
	p.Age = 10
}

func changeValue2(p *Person) {
	p.Age = 10
}

func main() {
	p := Person{Age: 1, Name: "HelloWorld"}
	changeValue(p)
	fmt.Printf("changeValue, name ->%v, age -> %v\n", p.Name, p.Age)
	changeValue2(&p)
	fmt.Printf("changeValue2, name ->%v, age -> %v\n", p.Name, p.Age)

	body, _ := json.Marshal(p)
	fmt.Printf("struct to json -> %v\n", string(body))

	newP := Person{}
	fmt.Printf("newP age -> %v\n", newP.Age)
	json.Unmarshal(body, &newP)
	fmt.Printf("after json to struct -> %v\n", newP.Age)
}

输出的结果为

[Running] go run "/ihome/go/golang-weibo-sdk/src/main/main.go"
changeValue, name ->HelloWorld, age -> 1
changeValue2, name ->HelloWorld, age -> 10
struct to json -> {"Name":"HelloWorld","Age":10}
newP age -> 0
after json to struct -> 10

[Done] exited with code=0 in 0.13 seconds

参数中是否是指针的区别:

package main

import "fmt"

// Person : test object
type Person struct {
	Name string
	Age  int
}

func changeValue(p Person) {
	p.Age = 10
}

func changeValue2(p *Person) {
	p.Age = 10
}

func main() {
	p := Person{Age: 1, Name: "HelloWorld"}
	changeValue(p)
	fmt.Printf("changeValue, name ->%v, age -> %v\n", p.Name, p.Age)
	changeValue2(&p)
	fmt.Printf("changeValue2, name ->%v, age -> %v\n", p.Name, p.Age)
}

输出的结果为

[Running] go run "/ihome/go/golang-weibo-sdk/src/main/main.go"
changeValue, name ->HelloWorld, age -> 1
changeValue2, name ->HelloWorld, age -> 10

[Done] exited with code=0 in 0.103 seconds

常用Go包

将struct转换为查询参数:
https://godoc.org/github.com/google/go-querystring/query

常见错误

http: ContentLength=xxx with Body length yyy

ContentLength=299 with Body length 367

这一般是request对象前后的长度不一致(中间关闭了或处理了,导致后面的再次读取时不一致)

参考: stackoverflow

具体例子:我是在构造request时这样子写:

	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)

	v := reflect.ValueOf(param)
	fileds := v.Elem()

	paramsMap := netURL.Values{}
	//defer bodyWriter.Close()

	return http.NewRequest("POST", URL, bodyBuf)

关键是那行:defer bodyWriter.Close(),注释掉这行就可以了.

cannot define new methods on non-local type

type MyRouter mux.Router

func (m *MyRouter) F() { ... }

或者

type MyRouter struct {
    *mux.Router
}

func (m *MyRouter) F() { ... }

...
r := &MyRouter{router}
r.F()

can’t load package: import cycle not allowed

package project/controllers/account
    imports project/controllers/base
    imports project/components/mux
    imports project/controllers/account

这表明有循环引用,循环引用的结果为:

                  project/controllers/account
                     ^                    \    
                    /                      \
                   /                        \ 
                  /                         \/
         project/components/mux <--- project/controllers/base

只要处理掉这个循环引用的一条链即可解决.

too many arguments to return

一般来说,是你的方法签名中没有返回值,但你却进行了return value或者,你的方法签名中的返回值个数与return 语句不匹配

*interface {} is pointer to interface, not interface

这个应该是你想在方法里进行泛型例如写成这样子:

package helper

import "reflect"

// SetDefaultValues : 设置参数默认值
func SetDefaultValues(param *interface{}) {
	v := reflect.ValueOf(param)
	fileds := v.Elem()
	for i := 0; i < fileds.NumField(); i++ {
		field := fileds.Field(i)
		// Ignore fields that don't have the same type as a string
		if field.Type().Name() == "string" {
			//如果是一个字符串对象
			if field.CanSet() {
				field.SetString("")
			}
		} else if field.Type().Name() == "int" || field.Type().Name() == "int64" {
			//如果是整型
			if field.CanSet() {
				field.SetInt(-1)
			}
		}
	}
}

但其实interface{}它表示的是接收任意的类型(包括指针),所以,这里不能这样子,应该修改参数为:param interface{}即可。

这样子,在调用方法时,传递的是指针,那么它就代表指针,传递的是普通类型,那就是普通类型。

cannot use nil as type xxx

这表你的xxx并不能接受nil值。(一般来说,只有指针类型才能接受nil值)

found packages xxxx () and xxxx (xxxx) in xxxx

┌─[sky@sky-linux] - [/ihome/go/golang-weibo-sdk/src/com.github.emacsist.weibosdk/api/business/status] - [2016-10-21 05:40:43]                              
└─[2] <git:(master 3c154e3✱✈) > go test -run StatusesRepostTimelineBizString                                                                               
../../../helper/HttpHelper.go:8:2: found packages resp (CommentsAndTotalNumberResp.go) and api (TagsTagsBatchOther.go) in /ihome/go/golang-weibo-sdk/src/co
m.github.emacsist.weibosdk/resp 

Go语言规则,所有在同一个目录的源文件,都必须是同属一个包的.解决了这个问题就可以编译通过了.

struct 结构体的继承

// Person : test object
type Person struct {
	Name string
	Age  int
	Animal
}

type Animal struct {
    Colour string
    Name string
}

Go中的字符串转换为字符数组

package main

import "fmt"

func main() {
    fmt.Println(string("Hello"[1]))              // ASCII only
    fmt.Println(string([]rune("Hello, 世界")[1])) // UTF-8
    fmt.Println(string([]rune("Hello, 世界")[8])) // UTF-8
}

千万要注意:string[0:1]这种形式是按字节来分隔的.len(string)返回的是字节数. 要操作字符的话,则要使用rune类型。

Golang中顶级的JSON数组字符串转换为json对象

package main

import "fmt"
import "encoding/json"

type PublicKey struct {
	Id  int
	Key string
}

func main() {
	keysBody := []byte(`[{"id": 1,"key": "-"},{"id": 2,"key": "-"},{"id": 3,"key": "-"}]`)
	keys := make([]PublicKey, 0)
	json.Unmarshal(keysBody, &keys)
	fmt.Printf("%#v", keys[2].Id)
}

创建一个自定义的error

import "errors"

errors.New("xx")

json数组转换为map

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	h := `[
    {
        "221012100001985342": "80后",
        "weight": 50
    },
     {
        "221012100001985342": "80后1",
        "weight": 51
    }
]`
	var hell []map[string]interface{}

	err := json.Unmarshal([]byte(h), &hell)
	fmt.Printf("%v\n", err)
	fmt.Printf("%v\n", h)
	fmt.Printf("%v\n", hell)
}

Go交叉编译

进入Go的安装目录cd $GOROOT/src,然后 选择要生成的目标构架的工具链:

# windows64位
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 ./make.bash

# mac
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 ./make.bash

# linux 64
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 ./make.bash

常见错误:

┌─[sky@sky-linux] - [/ihome/go/go-current/src] - [2016-10-26 02:14:42]
└─[0] <> CGO_ENABLED=0 GOOS=windows GOARCH=amd64 ./make.bash
##### Building Go bootstrap tool.
cmd/dist
ERROR: Cannot find /home/sky/go1.4/bin/go.
Set $GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4.

这表需要有一个>=1.4的另一个go安装目录,并将GOROOT_BOOTSTRAP指向该目录即可.

Linux下交叉编译Mac

stackoverflow

OS常量以及ARCH常量

syslist


package build

const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows zos "
const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64 "

Go打包静态资源

安装: go get -u github.com/jteeuwen/go-bindata/...

go-bindata

假设/tmp/go是一个$GOPATH:

┌─[sky@sky-linux] - [/tmp/go] - [2016-10-26 03:25:55]
└─[0] <> ll /tmp/go/src/nlp/dict
total 820K
-rw-r--r-- 1 sky sky 3.1K 10月 25 18:36 adv_full.txt
-rw-r--r-- 1 sky sky 3.2K 10月 25 18:36 adv.txt
-rw-r--r-- 1 sky sky 212K 10月 25 18:36 neg_full.txt
-rw-r--r-- 1 sky sky 305K 10月 25 18:36 neg.txt
-rw-r--r-- 1 sky sky  273 10月 25 18:36 not_full.txt
-rw-r--r-- 1 sky sky  287 10月 25 18:36 not.txt
-rw-r--r-- 1 sky sky 116K 10月 25 18:36 pos_full.txt
-rw-r--r-- 1 sky sky 149K 10月 25 18:36 pos.txt
-rw-r--r-- 1 sky sky 8.0K 10月 25 21:21 stp_full.txt
-rw-r--r-- 1 sky sky 8.0K 10月 25 18:36 stp.txt

┌─[sky@sky-linux] - [/tmp/go] - [2016-10-26 12:35:32]                                                                                                                      
└─[0] <> ./bin/go-bindata -o=src/nlp/asset/asset.go -pkg=asset -prefix "/tmp/go/src/nlp/" /tmp/go/src/nlp/dict

说明:

-o : 表示生成静态资源的访问包的文件. -pkg : 表示包名 -prefix : 表示自动添加的前缀。

/tmp/go/src/nlp/dict表示静态资源的目录.如果要递归的话,可以这样子写: /tmp/go/src/nlp/dict/...

成功后,可以看到生成了一个新的文件:/tmp/go/src/nlp/asset/asset.go

使用例子:

import (
	"nlp/asset"
)

func init() {
	fmt.Printf("init --> %v\n", asset.AssetNames())
	loadDict("dict/adv_full.txt", &advMap)
	loadDict("dict/neg_full.txt", &negMap)
	loadDict("dict/not_full.txt", &notMap)
	loadDict("dict/pos_full.txt", &posMap)
	loadDict("dict/stp_full.txt", &stpMap)
}

func loadDict(path string, dic *map[string]bool) {
	//"dict/adv_full.txt"
	data, _ := asset.Asset(path)
	bufferReader := bytes.NewReader(data)
	scan := bufio.NewScanner(bufferReader)
	for scan.Scan() {
		t := scan.Text()
		(*dic)[t] = true
	}
}

因为我们添加了-prefix "/tmp/go/src/nlp/", 所以,我们可以直接这样子: dict/adv_full.txt的路径,就可以通过asset.Asset("dict/adv_full.txt")来访问我们的静态资源了.

常用编译选项

输出所有可用编译选项:

go tool compile -help

去掉调试信息

go build -ldflags '-w'

查看优化的信息(内联 inline 信息、逃逸分析等)

go build -gcflags=-m 

数组与slice 区别

package main

import "fmt"

func main() {
	arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	sliceArray := arr[3:5]

	// 由此可知,slice是一种引用类型,因为它的第一个元素地址,与arr[3]的地址是一样的
	fmt.Printf("arr[3] address = %v\n", &arr[3])
	fmt.Printf("sliceArray[0] address = %v\n", &sliceArray[0])

	// len = 2
	fmt.Printf("len slice = %v\n", len(sliceArray))

	// cap = 7,cap的计算,是从第一个slice元素的位置,到原数组的结尾的长度.
	fmt.Printf("cap slice = %v\n", cap(sliceArray))

	// 因为sliceArray的cap为7,已经存在2个,那还可以容纳5个元素,这时引用的,还是arr数组,这样子append后,会修改arr的内容
	// arr 为 : [0 1 2 3 4 1 2 3 4 5]
	// sliceArray 为 : [3 4 1 2 3 4 5
	sliceArray = append(sliceArray, 1, 2, 3, 4, 5)

	// 当cap还在arr容纳范围之内时,slice指向的就是arr,通过打印他们的地址可知:
	fmt.Printf("arr[5] address = %v, value=%v\n", &arr[6], arr[6])
	fmt.Printf("sliceArray[2] address = %v, value=%v\n", &sliceArray[3], sliceArray[3])

	//但当sliceArray超过了cap时: [3 4 1 2 3 4 5 1 2 3 4 5]
	sliceArray = append(sliceArray, 1, 2, 3, 4, 5)
	// 可以发现,它指向了一个新的数组:因为他们的地址不同了.
	fmt.Printf("arr[5] address = %v, value=%v\n", &arr[6], arr[6])
	fmt.Printf("sliceArray[2] address = %v, value=%v\n", &sliceArray[3], sliceArray[3])
	fmt.Printf("cap slice = %v\n", cap(sliceArray))

	fmt.Printf("%v, %v\n", arr, sliceArray)
}

append超出cap之前与之后:

package main

import "fmt"

func main() {
	arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	sliceArray := arr[3:5]
	sliceArray2 := arr[3:5]

	fmt.Printf("%v, %v, %v\n", arr, sliceArray, sliceArray2)
	fmt.Printf("%v, %v, %v\n", &arr[3], &sliceArray[0], &sliceArray2[0])

	sliceArray = append(sliceArray, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)

	fmt.Printf("%v, %v, %v\n", arr, sliceArray, sliceArray2)
	fmt.Printf("%v, %v, %v\n", &arr[3], &sliceArray[0], &sliceArray2[0])
}

Go中使用 redis

package main

import (
	"flag"
	"time"

	"fmt"

	"github.com/garyburd/redigo/redis"
)

func newPool(server, password string) *redis.Pool {
	return &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server)
			if err != nil {
				return nil, err
			}
			if len(password) > 0 {
				if _, err := c.Do("AUTH", password); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if time.Since(t) < time.Minute {
				return nil
			}
			_, err := c.Do("PING")
			return err
		},
	}
}

var (
	pool          *redis.Pool
	redisServer   = flag.String("redisServer", ":6379", "")
	redisPassword = flag.String("redisPassword", "", "")
)

func main() {
	redisGet()
	redisPipline()
}

func redisGet() {
	redisConnection := pool.Get()
	defer redisConnection.Close()

	//如果这个不为空,表明获取链接时出错
	if redisConnection.Err() != nil {
		fmt.Printf("Hello World %v\n", redisConnection.Err().Error())
		return
	}

	val, err := redisConnection.Do("get", "hello2")
	if err == nil {
		fmt.Printf("Hello %v\n", string(val.([]uint8)))
	} else {
		fmt.Printf("happen error : %v\n", err.Error())
	}

}

func redisPipline() {
	redisConnection := pool.Get()
	defer redisConnection.Close()

	//如果这个不为空,表明获取链接时出错
	if redisConnection.Err() != nil {
		fmt.Printf("Hello World %v\n", redisConnection.Err().Error())
		return
	}

	redisConnection.Send("get", "hello")
	redisConnection.Send("get", "hello1")
	redisConnection.Send("get", "hello2")

	redisConnection.Flush()

	for i := 0; i < 3; i++ {
		data, err := redisConnection.Receive()

		if err == nil && redisConnection.Err() == nil {
			if data == nil {
				continue
			}
			fmt.Printf("Hello from pipline %v\n", string(data.([]uint8)))
			continue
		} else {
			fmt.Printf("Hello from pipline error %v\n", err.Error())
			break
		}
	}
	fmt.Printf("done\n")
}

func init() {
	flag.Parse()
	fmt.Printf("redis: server = %v, passwd = %v\n", *redisServer, *redisPassword)
	pool = newPool(*redisServer, *redisPassword)
	fmt.Printf("init redis connection pool ok\n")
}


注意,这个默认是db 0,如果想实现选择(虽然不推荐使用多个DB),可以按如下链接做:

stackoverflow

Go 中使用 gorm 与 mysql 交互

package main

import (
	"fmt"

	"time"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type WbStatus struct {
	ID       int64 `gorm:"primary_key"`
	Text     string
	CreateAt time.Time
}

func (WbStatus) TableName() string {
	return "wb_status"
}

var db *gorm.DB

func main() {
	if db == nil {
		panic("db is nil")
	}
	var wbstatus WbStatus
	fmt.Printf("tableName = %v\n", wbstatus.TableName())
	db.First(&wbstatus)

	fmt.Printf("rowId = %v\n", wbstatus.ID)
	fmt.Printf("createAt = %v\n", wbstatus.CreateAt)
	defer db.Close()
}

func init() {
	var err error
	db, err = gorm.Open("mysql", "root:yang@(127.0.0.1:3306)/test?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		fmt.Printf("init mysql error : %v\n", err.Error())
		panic("exit")
	}
	if db == nil {
		fmt.Printf("init mysql error : db is null\n")
		panic("exit")
	} else {
		fmt.Printf("init mysql  : db isnot null\n")
	}
	db.DB().SetMaxOpenConns(100)
	db.DB().SetMaxIdleConns(10)
	fmt.Printf("init mysql OK\n")
}

gorm

go 中使用 rabbitmq

github amqp

whether-to-create-connection-every-time-when-amqp-dial-is-threadsafe-or-not-in-golang

ensuring-rabbitmq-connection-in-golang

注意事项

ch, _ := mqCon.Channel()
	msgs, err := ch.Consume(
		config.Configuration.Rabbit.Listen,     // queue
		config.Configuration.Rabbit.ConsumerID, // consumer
		true,  // auto-ack
		false, // exclusive
		false, // no-local
		false, // no-wait
		nil,   // args
	)

如果你将auto-ack设置为true,则不用手工确认消息。如果设置为false,则必须手工调用:

Delivery.Ack, Delivery.Reject or Delivery.Nack

rabbitmq-best-practices-in-go

autoAck:

When autoAck (also known as noAck) is true, the server will acknowledge deliveries to this consumer prior to writing the delivery to the network. When autoAck is true, the consumer should not call Delivery.Ack. Automatically acknowledging deliveries means that some deliveries may get lost if the consumer is unable to process them after the server delivers them.

go中long与timestamp json的转换

convert-to-time-in-golang-from-milliseconds

type A B只是让A获得B的内存模型而已。A不会继承B的方法,但A和B可以使用A()或B()来互相转换。

golangtc

Go 中找出实现了某接口的代码

egrep -nr '^func (.*) ReadByte\(' *

Golang中实现优雅关闭程序

func main() {

	sigs := make(chan os.Signal, 1)
	done := make(chan bool, 1)

	// sigs 表示将收到的信号放到这个管道中。后面的参数表示你想处理的系统信号
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	//开启一个Go routine来监听信号
	go func() {
		sig := <-sigs
		done <- true
	}()

	//这里添加你的程序的功能(监听器,处理器等)
	fmt.Println("awaiting signal")
	<-done
	fmt.Println("exiting")
}

常见信号:

Signal Name	Number	Description
SIGHUP	1	Hangup (POSIX)
SIGINT	2	Terminal interrupt (ANSI)
SIGQUIT	3	Terminal quit (POSIX)
SIGILL	4	Illegal instruction (ANSI)
SIGTRAP	5	Trace trap (POSIX)
SIGIOT	6	IOT Trap (4.2 BSD)
SIGBUS	7	BUS error (4.2 BSD)
SIGFPE	8	Floating point exception (ANSI)
SIGKILL	9	Kill(can't be caught or ignored) (POSIX)
SIGUSR1	10	User defined signal 1 (POSIX)
SIGSEGV	11	Invalid memory segment access (ANSI)
SIGUSR2	12	User defined signal 2 (POSIX)
SIGPIPE	13	Write on a pipe with no reader, Broken pipe (POSIX)
SIGALRM	14	Alarm clock (POSIX)
SIGTERM	15	Termination (ANSI)
SIGSTKFLT	16	Stack fault
SIGCHLD	17	Child process has stopped or exited, changed (POSIX)
SIGCONT	18	Continue executing, if stopped (POSIX)
SIGSTOP	19	Stop executing(can't be caught or ignored) (POSIX)
SIGTSTP	20	Terminal stop signal (POSIX)
SIGTTIN	21	Background process trying to read, from TTY (POSIX)
SIGTTOU	22	Background process trying to write, to TTY (POSIX)
SIGURG	23	Urgent condition on socket (4.2 BSD)
SIGXCPU	24	CPU limit exceeded (4.2 BSD)
SIGXFSZ	25	File size limit exceeded (4.2 BSD)
SIGVTALRM	26	Virtual alarm clock (4.2 BSD)
SIGPROF	27	Profiling alarm clock (4.2 BSD)
SIGWINCH	28	Window size change (4.3 BSD, Sun)
SIGIO	29	I/O now possible (4.2 BSD)
SIGPWR	30	Power failure restart (System V)

检查程序监控了哪些信号

signals

Golang中的struct tag使用

package main

import (
	"fmt"
	"reflect"
)

// Person :
type Person struct {
	Name string `mytag:"HelloName"`
}

func main() {
	p := Person{Name: "emacsist"}
	refValue := reflect.ValueOf(&p)

	fields := refValue.Elem()
	for i := 0; i < fields.NumField(); i++ {
		field := fields.Field(i)
		fieldName := fields.Type().Field(i).Name
		fieldValue := field.Interface()
		fieldTag := fields.Type().Field(i).Tag.Get("mytag")
		fmt.Printf("fieldName = %v, fieldValue = %v, tagName = %v\n", fieldName, fieldValue, fieldTag)
	}
}

限制Goalng的HTTP 工作线程(goroutine)

	connectionCount := 5000

	l, err := net.Listen("tcp", ":9090")

	if err != nil {
		log.Fatalf("Listen: %v", err)
	}

	l = netutil.LimitListener(l, connectionCount)
	log.Fatal(http.Serve(l, nil))
	logrus.Infof("start server success. %v", ListenAddress)

为什么我的内存没有被操作系统回收?

stackoverflow

golang

Golang1.8 中的优雅关闭 http


	s := &http.Server{
		Addr: ":9090",
	}
	go func() {
		log.Infof("%s", s.ListenAndServe())
	}()
	// Handle SIGINT and SIGTERM.
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	log.Println(<-ch)

	log.Println("http server gracefully stopping...")
	// Stop the service gracefully.
	s.Shutdown(context.Background())
	log.Println("http server gracefully shutdown done...")

按 github 风格的项目组织结构

github code layout

压缩编译后的文件大小

去掉符号信息

go build -ldflags "-s -w"

使用 upx 压缩

upx -9 GoBinaryFile

Golang 中的 web

在 web 中使用 panic 只会退出当前的 goroutine ,而不会整个应用程序退出。

在一般应用中,如果没有捕捉 panic() 的话,就会导致整个应用退出:

package main

import (
	"sync"
	"time"
)

func main() {
	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		time.Sleep(5 * time.Second)
		println("Hello World")
		panic("==================>")
	}()

	go func() {
		time.Sleep(2 * time.Second)
		println("Hello World2")
		panic("==================>2")
	}()
	wg.Wait()
}

短声明注意

短声明只能声明局部变量,而且它会覆盖外部的同名变量。

所以,如果目的是想进行外部变量的初始化的话,这点可能没达到我们想要的目的,这点要特别注意。

类型转换

不论显式还是隐式的 常量 的类型转换,常量从一种类型转换为另一种类型,都要求目标类型能够表示 原值

Golang 中格式化日期

YYYY-MM-DD HH:mm:ss 
对应
2006-01-02 15:04:05

stackoverflow.com

package main

import (
    "golang.org/x/net/publicsuffix"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/cookiejar"
)

func main() {
    options := cookiejar.Options{
        PublicSuffixList: publicsuffix.List,
    }
    jar, err := cookiejar.New(&options)
    if err != nil {
        log.Fatal(err)
    }
    client := http.Client{Jar: jar}
    resp, err := client.Get("http://dubbelboer.com/302cookie.php")
    if err != nil {
        log.Fatal(err)
    }
    data, err := ioutil.ReadAll(resp.Body)
    resp.Body.Close()
    if err != nil {
        log.Fatal(err)
    }
    log.Println(string(data))
}

Go 中提交表单


	form := url.Values{}
	form.Add("qps", strconv.FormatInt(qps, 10))


	setQPSRequest, err := http.NewRequest("POST", loginURL, strings.NewReader(form.Encode()))
	if err != nil {
		return
	}

	setQPSRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")

logrus 设置日志时间格式

logrus 设置日期

func init() {
	formatter := &logrus.TextFormatter{
		FullTimestamp: true,
	}
	logrus.SetFormatter(formatter)
}

Go 中自动重连 RabbitMQ

var (
	connectionDone = make(chan *amqp.Error)
	conn           *amqp.Connection
	channel        *amqp.Channel
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
	}
}

func init() {
	connectToRabbitMQ()
	go rabbitConnector()
}

func connectToRabbitMQ() *amqp.Connection {
	for {
		conn, err := amqp.Dial(config.AppConfig.GetAmqpUrl())

		if err == nil {
			if channel != nil {
				//要先关闭之前的 channel
				log.Warnf("close before channel")
				channel.Close()
			}
			channel, _ = conn.Channel()
			log.Infof("conn address = %p, channel address =%p", config.AppConfig.GetAmqpUrl(), channel)
			return conn
		}
		log.Errorf("error connect to rabbitmq. %s", err.Error())
		log.Warnf("Trying to reconnect to RabbitMQ at %v", config.AppConfig.GetAmqpUrl())
		time.Sleep(500 * time.Millisecond)
	}
}

func rabbitConnector() {
	for {
		closeEvent := <-connectionDone
		if closeEvent != nil {
			log.Errorf("receive connection closed event!")
			log.Errorf("Connecting to %v", config.AppConfig.GetAmqpUrl())
			//每一次创建时,都要用新的 chan 对象来接收通知。否则会报 panic: send on closed channel
			// update 2017-4-14
			connectionDone = make(chan *amqp.Error)
			conn = connectToRabbitMQ()
			conn.NotifyClose(connectionDone)
		}
	}
}

Go 中批量处理 SQL

	insertStatement := "INSERT OR  REPLACE INTO indice (index_name, static_pe, pe_ttm, static_pb, sse, last_year_static_pe, last_year_pe_ttm, last_year_pb, statis_date) VALUES "
	vals := []interface{}{}

	for _, row := range rows {
		insertStatement += "(?, ?, ?, ?, ?, ?, ?, ?, ?),"
		vals = append(vals, row.Index_name, row.Static_pe, row.Pe_ttm, row.Static_pb, row.Sse, row.Last_year_static_pe, row.Last_year_pe_ttm, row.Last_year_pb, row.Pe_date)
		if Debug {
			log.Infof("row => %v", row)
		}
	}
	//删除最后的 ,
	insertStatement = insertStatement[0:len(insertStatement)-1]
	//prepare the statement

	if Debug {
		log.Infof("insertStatement => %v", insertStatement)
	}

	stmt, err := db.Prepare(insertStatement)

	if err != nil {
		panic(err)
	}

	res, _ := stmt.Exec(vals...)

	effectRow, _ := res.RowsAffected()
	fmt.Printf("done.  affected %v", effectRow)

Go 中处理 GBK 字符集

导入

"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
	robots, err := ioutil.ReadAll(response.Body)

	sr := strings.NewReader(string(robots))
	tr := transform.NewReader(sr, simplifiedchinese.GB18030.NewDecoder())
	b, _ := ioutil.ReadAll(tr)
	result := string(b)
	return result

Go 中 GET 请求的参数处理

	req, err := http.NewRequest(http.MethodGet, CsindexUrl, nil)
    q := req.URL.Query()
	q.Add("参数名", "参数值")
	req.URL.RawQuery = q.Encode()

    # 添加 cookie
    cookie1 := &http.Cookie{
		Name:  "XSRF-TOKEN",
		Value: "eyJpdiI6ImYrZVwvM2k3YlF4NDhIRG1DU3A0cXNRPT0iLCJ2YWx1ZSI6IkdLc3NYUDBidVd3V3JUamY0a1FSb1pBVGxiVmxBYTRHRHBxTDRmUW92V2hhMjE1bUpPMGFOUStJSVg3RmdvdXQ0eFgrRDliSElod3hHdWRlRFwvbGo2Zz09IiwibWFjIjoiOTNjNWQyZjk0YjFjYWJjZTc3ZGE1ZjQ1Y2IyZGQzNmVjYTI3YWRiNDRmNjg2MjBkMDEzMGE2ODhkZTBmZTJhNiJ9",
	}
	cookie2 := &http.Cookie{
		Name:  "laravel_session",
		Value: "eyJpdiI6ImZzTlJMb0NZVHMwRElqcGYzd2FmZ0E9PSIsInZhbHVlIjoiRktjN2xNczl0Z0hlN09MMzRRcmlcL1RNazZQRXJhT1FTWnh1V2o3TzVnTGhcL2RVMys5aHAwRmxyTlBJRWFramlzd29KRnpOMFcxVW1kanVxZDEzajJ1UT09IiwibWFjIjoiNmJlOTFiMDkxY2NkOTkyNGI2NzdjYmM0YmI3MWZmYmU1NDI0OWI2ZDcwZjc0NTdjNDU1NDQ1YjM0YzJhMTc0OSJ9",
	}

	req.AddCookie(cookie1)
	req.AddCookie(cookie2)

    response, err := http.DefaultClient.Do(req)

Go 中调试 http 的 request 和 response

这个类似 curl -v 的参数.

这个要导入包 "net/http/httputil"


debug(httputil.DumpRequestOut(req, true))


debug(httputil.DumpResponse(response, true))

func debug(data []byte, err error) {
	if err == nil {
		fmt.Printf("%s\n\n", data)
	} else {
		log.Fatalf("%s\n\n", err)
	}
}

Go 中按行读取 string

	scanner := bufio.NewScanner(strings.NewReader(bodyString))
	for scanner.Scan() {
		line := scanner.Text()
    }

Go 中按空格 split 字符串

fields := strings.Fields(str)

读取 json 文件为 struct 结构体

这里的路径是从 二进制文件所在的根目录下开始读取 filePath

// readJSON: 从 filePath 里读取数据, 并转换为 jsonObject 对象
func readJSON(filePath string, jsonObject interface{}) {
	appPath, e := os.Executable()
	if e != nil {
		logrus.Errorf("File error: %v\n", e)
		os.Exit(1)
	}

	file, e := ioutil.ReadFile(path.Dir(appPath) + "/" + filePath)
	if e != nil {
		logrus.Errorf("File error: %v\n", e)
		os.Exit(1)
	}
	e = json.Unmarshal(file, jsonObject)
	if e != nil {
		logrus.Errorf("invalid json data error: %v\n", e)
		os.Exit(1)
	}
}

string 与 int 互转

int -> string
strconv.Itoa(e.Port)

string -> int
v, _ = strconv.Atoi(str)

Go 中 http client 的 timeout 设置

timeout := time.Duration(5 * time.Second)
client := http.Client{
    Timeout: timeout,
}
client.Get(url)