Go学习
Contents
安装多个版本的 go
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结构
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
OS常量以及ARCH常量
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/...
假设/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", ¬Map)
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),可以按如下链接做:
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")
}
go 中使用 rabbitmq
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
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()来互相转换。
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)
检查程序监控了哪些信号
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)
为什么我的内存没有被操作系统回收?
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 风格的项目组织结构
压缩编译后的文件大小
去掉符号信息
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
Go 中发送登录请求(保持 cookie)
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)