Golang 的一些笔记
Golang 多版本安装
method1 官方
$ go get -u golang.org/dl/go1.12.3 go: finding golang.org/dl latest go: downloading golang.org/dl v0.0.0-20190408222801-b337094d5ff3 go: extracting golang.org/dl v0.0.0-20190408222801-b337094d5ff3 $ go1.12.3 download Downloaded 100.0% (127615731 / 127615731 bytes) Unpacking /Users/bingoobjca/sdk/go1.12.3/go1.12.3.darwin-amd64.tar.gz ... Success. You may now run 'go1.12.3' $ go1.12.3 version go version go1.12.3 darwin/amd64 $ go version go version go1.12.1 darwin/amd64
method2 gvm
install bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
influxdb 推荐使用 gvm 来构建:
$ gvm install go1.15 $ gvm use go1.15 --default
相关书籍
- 《Golang修养之路》本书针对Golang专题性热门技术深入理解,修养在Golang领域深入话题,脱胎换骨。
- Go语言圣经(中文版)
- Modern Go Programming DSL for Query: use SQL to query in memory collection, and any databases
- Essential Go
- Go语言高级编程(Advanced Go Programming)
- Go语言101 一本着墨于Go语法和语义的编程指导书
- 深入Go并发编程研讨课
- go教程电子书
- 深入解析Go
- Go 语言中文开源图书、资料或文档
- Go专家编程
- Go语言爱好者周刊
- Uber Go语言开发规范
- gopherchina/conference Golang conference PPT
- Effective Go, Go Code Review Comments
- 煎鱼的迷之博客 跟煎鱼学go
- Go 语言设计与实现
- Christmas 2020 Go class slides
无缓冲读
package main import "fmt" func main() { // create unbuffered channel of int values with capacity of 1 ch := make(chan int) select { case ch <- 3: // uncomment the following line to get this program work // default: } fmt.Printf("ok\n") }
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/056148531/main.go:10 +0x60
exit status 2
一些库
一些工具链
dogsled a Go static analysis tool to find assignments/declarations with too many blank identifiers (e.g. x, _, _, _, := f()). Its name was inspired from this reddit post.
包管理
经常看到go.mod文件中,有一些indirect的包,是怎么被依赖进来的,可以用命令 go mod why yourAwesomePackage
来查看。比如:
# bingoo @ 192 in ~/GitHub/loglineparser on git:master x [12:40:02] $ go mod why gopkg.in/yaml.v2 # gopkg.in/yaml.v2 github.com/bingoohuang/loglineparser github.com/araddon/dateparse github.com/araddon/dateparse.test github.com/simplereach/timeutils gopkg.in/mgo.v2/bson gopkg.in/mgo.v2/bson.test gopkg.in/yaml.v2
As of the final 1.11 release, the go module cache (used for storing downloaded modules and source code), is in the $GOPATH/pkg/mod location (see the docs here). For clarification, the go build cache (used for storing recent compilation results) is in a different location.
This article, indicated that it's in the $GOPATH/src/mod, but in the timespan of the recent ~40 days, the golang team must have changed that target location. This message thread has some discussion on why the downloaded items ended up in $GOPATH/pkg.
You can also use the go mod download -json command to see the downloaded modules/source metadata and their location on your local disk. Example output below:
$ go mod download -json
go: finding github.com/aws/aws-sdk-go v1.14.5
go: finding github.com/aws/aws-lambda-go v1.2.0
{ "Path": "github.com/aws/aws-lambda-go", "Version": "v1.2.0", "Info": "/go/pkg/mod/cache/download/github.com/aws/aws-lambda-go/@v/v1.2.0.info", "GoMod": "/go/pkg/mod/cache/download/github.com/aws/aws-lambda-go/@v/v1.2.0.mod", "Zip": "/go/pkg/mod/cache/download/github.com/aws/aws-lambda-go/@v/v1.2.0.zip", "Dir": "/go/pkg/mod/github.com/aws/aws-lambda-go@v1.2.0", "Sum": "h1:2f0pbAKMNNhvOkjI9BCrwoeIiduSTlYpD0iKEN1neuQ=", "GoModSum": "h1:zUsUQhAUjYzR8AuduJPCfhBuKWUaDbQiPOG+ouzmE1A=" } { "Path": "github.com/aws/aws-sdk-go", "Version": "v1.14.5", "Info": "/go/pkg/mod/cache/download/github.com/aws/aws-sdk-go/@v/v1.14.5.info", "GoMod": "/go/pkg/mod/cache/download/github.com/aws/aws-sdk-go/@v/v1.14.5.mod", "Zip": "/go/pkg/mod/cache/download/github.com/aws/aws-sdk-go/@v/v1.14.5.zip", "Dir": "/go/pkg/mod/github.com/aws/aws-sdk-go@v1.14.5", "Sum": "h1:+l1m6QH6LypE2kL0p/G0Oh7ceCv+IVQ1h5UEBt2xjjU=", "GoModSum": "h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k=" }
JSON
JSON 数据解析,直接给字段赋予 json 字符串,避免转义
package main import ( "encoding/json" "fmt" ) func main() { s := `[` + `{"name":"bingoo"},{"name":"dingoo"}` + `]` var arr []interface{} if err := json.Unmarshal([]byte(s), &arr); err != nil { panic(err) } m := map[string]interface{}{"key1": arr, "key2": s, "key3": json.RawMessage([]byte(s))} jso, err := json.Marshal(m) if err != nil { panic(err) } // {"key1":[{"name":"bingoo"},{"name":"dingoo"}],"key2":"[{\"name\":\"bingoo\"},{\"name\":\"dingoo\"}]","key3":[{"name":"bingoo"},{"name":"dingoo"}]} fmt.Println(string(jso)) }
JSON解析库
- 流式 JSON 解析库 GoJay aims to be a very fast, JIT stream parser with 0 reflection, low allocation with a friendly API.
Alternate implementations
Popular replacements for standard library packages:
- encoding/json -> ffjson, easyjson, jingo (only encoder), etc
- net/http
- fasthttp (but incompatible API, not RFC compliant in subtle ways)
- httprouter (has other features besides speed; I've never actually seen routing in my profiles)
- regexp -> ragel (or other regular expression package)
- serialization
- encoding/gob -> alecthomas/go_serialization_benchmarks
- protobuf -> gogo/protobuf
- all serialization formats have trade-offs: choose one that matches what you need
- Write heavy workload -> fast encoding speed
- Read-heavy workload -> fast decoding speed
- Other considerations: encoded size, language/tooling compatibility
- tradeoffs of packed binary formats vs. self-describing text formats
- database/sql -> has tradeoffs that affect performance
- look for drivers that don't use it: jackx/pgx, crawshaw sqlite, ...
- gccgo (benchmark!), gollvm (WIP)
- container/list: use a slice instead (almost always)
- gojsonq A simple Go package to Query over JSON Data. It provides simple, elegant and fast ODM like API to access, query JSON document
import "github.com/thedevsaddam/gojsonq" func main() { const json = `{"name":{"first":"Tom","last":"Hanks"},"age":61}` name := gojsonq.New().FromString(json).Find("name.first") println(name.(string)) // Tom }
强制确保类型实现某个接口
Go 语言中,类型实现某个接口 ,只要实现了该接口中所有定义的方法即可,没有像 Java 中的 implements 关键字,是一种契约式精神的体现,或者说鸭子类型。那有没有强制某个类型必须某个接口的写法呢,刚刚在翻阅 jons.RawMessage 中看到了以下两行代码,刚开始感觉迷惑,仔细思考后,便了解了其意图:
var _ Marshaler = (*RawMessage)(nil) var _ Unmarshaler = (*RawMessage)(nil)
带上下文的代码如下:
// RawMessage is a raw encoded JSON value. // It implements Marshaler and Unmarshaler and can // be used to delay JSON decoding or precompute a JSON encoding. type RawMessage []byte // MarshalJSON returns m as the JSON encoding of m. func (m RawMessage) MarshalJSON() ([]byte, error) { if m == nil { return []byte("null"), nil } return m, nil } // UnmarshalJSON sets *m to a copy of data. func (m *RawMessage) UnmarshalJSON(data []byte) error { if m == nil { return errors.New("json.RawMessage: UnmarshalJSON on nil pointer") } *m = append((*m)[0:0], data...) return nil } var _ Marshaler = (*RawMessage)(nil) var _ Unmarshaler = (*RawMessage)(nil)
在 for 循环里不要使用 select + time.After 的组合,有坑
本来想给朋友宣称一下,西游可能有 Bug,我都已经被挤下线了,但是还能继续谷歌(假百度),然后随便输入了一个 golang oom 作为搜索条件:
结果就把这个搜索结果页面扔到一边,去干其他事情去了。
等回过头来,打算关闭这个标签页的时候,发现结果 第一条,还是有点兴趣的,就点进去看了,最后有这么一句话:
有经验的 gopher 都知道,在 for 循环里不要使用 select + time.After 的组合,有坑。
当使用 golang 过程中,遇到性能和内存 gc 问题,都可以使用 golang tool pprof 来排查分析问题。
然后,想到自己也经常这么用(for select time.After),很可能踩坑。然后去继续探索了一下,为什么可能会有坑 ,然后将代码中的for select time.After全部重构成 time.Timer + for来预防坑。
Don't use time.Tick to prevent leaks
就是因为在循环中,不停的创建新的计时器,而每个计时器都会开启内部协程。再看看计时器函数的官方注释:
// Tick is a convenience wrapper for NewTicker providing access to the ticking // channel only. While Tick is useful for clients that have no need to shut down // the Ticker, be aware that without a way to shut it down the underlying // Ticker cannot be recovered by the garbage collector; it "leaks". // Unlike NewTicker, Tick will return nil if d <= 0. func Tick(d Duration) <-chan Time { if d <= 0 { return nil } return NewTicker(d).C }
一些代码检查工具
golangci-lint run
来自这里,安装GO111MODULE=on go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.16.0
- 一些定制
golangci-lint run --exclude="cyclomatic complexity" --exclude-use-default=false --enable=golint --enable=gocyclo --enable=goconst --enable=unconvert ./...
golangci-lint run --enable-all
- Staticcheck – a collection of static analysis tools for working with Go code,安装
GO111MODULE=off go get honnef.co/go/tools/cmd/...
,运行staticcheck ./...
- 像牛人一样改进你的Go代码
- Go Report Card,golang进阶:怎么开发一个热门的开源项目
- A tool to list and diagnose Go processes currently running on your system
- golang 进阶: go1.12 mod 教程
检查依赖是否有升级
go get -u github.com/psampaz/go-mod-outdated
go list -u -m -json all | go-mod-outdated -direct
example output
+---------------------------------+----------------------------------------------------------------+------------------------------------+--------+------------------+
| MODULE | VERSION | NEW VERSION | DIRECT | VALID TIMESTAMPS |
+---------------------------------+----------------------------------------------------------------+------------------------------------+--------+------------------+
| github.com/alecthomas/template | v0.0.0-20160405071501-a0175ee3bccc | | true | true |
| github.com/bingoohuang/gou | v0.0.0-20190604082926-bf3d9b2b55aa5840c442284656ed6b15aedc5a25 | v0.0.0-20190604082926-be6100942b5a | true | false |
| github.com/bingoohuang/now | v0.0.0-20190604021600-70970d3ad0e7 | | true | true |
| github.com/bingoohuang/statiq | v0.2.1 | | true | true |
| github.com/dgraph-io/badger | v2.0.0-rc.2+incompatible | v1.5.4 | true | false |
| github.com/gchaincl/dotsql | v0.1.0 | | true | true |
| github.com/gin-contrib/sessions | v0.0.0-20190226023029-1532893d996f | v0.0.0-20190512062852-3cb4c4f2d615 | true | true |
| github.com/gin-gonic/gin | v1.4.0 | | true | true |
| github.com/go-sql-driver/mysql | v1.4.1 | | true | true |
| github.com/lib/pq | v1.0.0 | v1.1.1 | true | true |
| github.com/mattn/go-sqlite3 | v1.10.0 | | true | true |
| github.com/patrickmn/go-cache | v2.1.0+incompatible | | true | true |
| github.com/pkg/errors | v0.8.1 | | true | true |
| github.com/sirupsen/logrus | v1.4.2 | | true | true |
| github.com/spf13/pflag | v1.0.3 | | true | true |
| github.com/spf13/viper | v1.3.2 | v1.4.0 | true | true |
| github.com/stretchr/testify | v1.3.0 | | true | true |
| github.com/swaggo/swag | v1.4.1 | v1.5.0 | true | true |
| github.com/thoas/go-funk | v0.4.0 | | true | true |
+---------------------------------+----------------------------------------------------------------+------------------------------------+--------+------------------+
Golang 中的对象健美操
- One level of indentation per method.
- Don't use the ELSE keyword.
- Wrap all primitives and Strings in classes.
- First class collections.
- One dot per line.
- Don't abbreviate.
- Keep all classes less than 50 lines.
- No classes with more than two instance variables.
- No getters or setters.
Go linux 安装
下载,解压缩
[vagrant@bogon ~]$ curl -O https://dl.google.com/go/go1.12.5.linux-amd64.tar.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 122M 100 122M 0 0 349k 0 0:05:57 0:05:57 --:--:-- 356k [vagrant@bogon ~]$ tar -xzf go1.12.5.linux-amd64.tar.gz [vagrant@bogon ~]$ ls base.sh cleanup.sh go go1.12.5.linux-amd64.tar.gz puppet.sh vagrant.sh virtualbox.sh zerodisk.sh
设置环境变量 PATH
[vagrant@bogon ~]$ echo "export PATH=\$PATH:/home/vagrant/go/bin" >> ~/.bashrc; source ~/.bashrc [vagrant@bogon ~]$ go version go version go1.12.5 linux/amd64
Go1.13 将正式开始 Go2 开发历程
首先是执行 go get golang.org/dl/gotip
安装 tip 的辅助命令,然后通过执行 gotip download
下载真正的 tip 版本工具。下载完成之后,就可以通过 totip 命令来编译和运行 Go 程序了。
➜ kerb git:(master) ✗ go get golang.org/dl/gotip go: finding golang.org/dl/gotip latest go: finding golang.org/dl latest go: downloading golang.org/dl v0.0.0-20190507014322-219d744c5398 go: extracting golang.org/dl v0.0.0-20190507014322-219d744c5398 ➜ kerb git:(master) ✗ gotip download Cloning into '/Users/bingoobjca/sdk/gotip'... remote: Counting objects: 9475, done remote: Finding sources: 100% (9475/9475) remote: Total 9475 (delta 1010), reused 5862 (delta 1010) package main Receiving objects: 100% (9475/9475), 22.11 MiB | 363.00 KiB/s, done. Resolving deltas: 100% (1010/1010), done. Checking out files: 100% (8598/8598), done. HEAD is now at f2a4c13 errors: clarify doc for As Building Go cmd/dist using /usr/local/go. Building Go toolchain1 using /usr/local/go. Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1. Building Go toolchain2 using go_bootstrap and Go toolchain1. Building Go toolchain3 using go_bootstrap and Go toolchain2. Building packages and commands for darwin/amd64. --- Installed Go for darwin/amd64 in /Users/bingoobjca/sdk/gotip Installed commands in /Users/bingoobjca/sdk/gotip/bin Success. You may now run 'gotip'! ➜ kerb git:(master) ✗ gotip version go version devel +f2a4c13 Tue Jun 11 21:50:05 2019 +0000 darwin/amd64
GOPROXY
# Enable the go modules feature export GO111MODULE=on # Set the GOPROXY environment variable export GOPROXY=https://goproxy.io
export GO111MODULE=on export GOPROXY=https://proxy.golang.org
GOPROXY=direct,https://127.0.0.1:12333,https://goproxy.cn,https://goproxy.io,https://mirrors.aliyun.com/goproxy,https://athens.azurefd.net go env -w GOSUMDB="off"
Go 语言诞生时,我们称它为系统编程语言,我有点遗憾,因为很多人因此认为它是一种操作系统编写语言。我们应该称它为服务编写语言,这是我们真正想做的。现在我想明白了,Go 是云基础架构语言,因为系统编程的另一个定义是云中运行的东西。
When we first announced Go, we called it a systems programming language, and I slightly regret that because a lot of people assumed it was an operating systems writing language. What we should have called it is a server writing language, which is what we really thought of it as. Now I understand that what we have is a cloud infrastructure language. Another definition of systems programming is the stuff that runs in the cloud.
-- Rob Pike
今天我探索了一下 golang 的两个方面:
- 类似于 Java 中有很好的 Builder 模式
- Getter 和 Setter 的命名约定
然后我发现了函数选项(Functional Options)模式, GIST上有一个最小的例子。
函数选项模式是由Rob Pike提出,并由Dave Cheney等推广开,它优雅地解决了go语言中默认参数问题。
下面总结一下,函数选项模式有哪些优点:
- 支持默认参数:不必向结构体参数那样,不使用时仍必须传递一个空的struct值
- 代码简洁:即使是像go-micro这种支持如此繁多选项,代码也很美观
- 扩展性好:增加新的选项只需少量代码
推而广之:类似结构体中变量的赋值都可以效仿之。
- Using functional options instead of method chaining in Go中,以gorm为示例,使用函数选项为例,改造了gorm的用法。
- Fluent Middleware in golang 使用了类似的Fluent模式。
- 类型安全的Reusable and type-safe options for Go API
溜开源项目
- gorm, GORM 中文文档
- Converts a database into gorm structs and RESTful api
- Converts a mysql table into a golang struct
- go-queryset 100% type-safe ORM for Go with code generation and MySQL/PostgreSQL/Sqlite3/SQL Server support. GORM adapted.
- Loukoum is a simple SQL Query Builder
- gosql based on sqlx, It's simple and keep simple
- go-tagexpr An interesting go struct tag expression syntax for field validation, etc.
- graceful reload golang http server, zero downtime, compatible with systemd, supervisor
- go-daemon Build Status GoDoc Library for writing system daemons in Go.,涉及lockfile
- tableflip Graceful process restarts in Go
- MinIO is a high performance object storage server compatible with Amazon S3 APIs
- RadonDB is an open source, cloud-native MySQL database for building global, scalable cloud services
创建的对象,里面已经启动了一个后台的死循环的 go 协程,当对象不再被使用时,因为背后的go协程还一直在跑,导致对象不能被gc回收,咋办?,参见 go-cache 的实现
func New(defaultExpiration, cleanupInterval time.Duration) *Cache { items := make(map[string]Item) return newCacheWithJanitor(defaultExpiration, cleanupInterval, items) } func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache { c := newCache(de, m) // This trick ensures that the janitor goroutine (which--granted it // was enabled--is running DeleteExpired on c forever) does not keep // the returned C object from being garbage collected. When it is // garbage collected, the finalizer stops the janitor goroutine, after // which c can be collected. C := &Cache{c} if ci > 0 { runJanitor(c, ci) runtime.SetFinalizer(C, stopJanitor) } return C } func runJanitor(c *cache, ci time.Duration) { j := &janitor{ Interval: ci, stop: make(chan bool), } c.janitor = j go j.Run(c) }
以库为驱动开发的 Golang 工程结构,摘自Library driven development
Moving the main.go file out of your root allows you to build your application from the perspective of a library. Your application binary is simply a client of your application’s library.
Sometimes you might want users to interact in multiple ways so you create multiple binaries.
For example, if you had an “adder” package that that let users add numbers together, you may want to release a command line version as well as a web version.
You can easily do this by organizing your project like this:
adder/
adder.go
cmd/
adder/
main.go
adder-server/
main.go
Users can install your “adder” application binaries with “go get” using an ellipsis:
$ go get github.com/benbjohnson/adder/...
And voila, your user has “adder” and “adder-server” installed!
稍微复杂一点的结构,摘自 Go project structure to produce library and cli with the same name in single repository
Of course if your project is more complex, you may create further packages under the project root, and it may have multiple commands (multiple main packages), e.g.:
host.com/project/
cmd/
project/
project.go
prjtool/
prjtool.go
packagex/
x.go
packagey/
y.go
filea.go
fileb.go
An example following this layout is the very popular Go Delve debugger (4.5k stars currently).
Passing callbacks and pointers to Cgo
This post discusses an end-to-end example that covers:
- Basic usage of Cgo, including linking a custom C library into the Go binary.
- Passing structs from Go to C.
- Passing Go functions to C and arranging C to call them back later.
- Safely passing arbitrary Go data to C code, which can later pass it back to the Go callbacks it invokes.
The full source code for this example is available on Github. cgo-callback.zip
宽进严出原则:
Be conservative in what you send, be liberal in what you accept
- Robustness
- Principle
- 在 golang 上的应用:
Return concrete types, receive interfaces as parameters
选哪个?
func New() *os.File
func New() io.ReadWriteCloser
func New() io.Writer
func New() interface{}
根据 宽进严出
: 选 func New() *os.File
看 goroutine 的数量
➜ golang-trial git:(master) ✗ GODEBUG=schedtrace=1000 gohttpd
SCHED 0ms: gomaxprocs=12 idleprocs=10 threads=4 spinningthreads=1 idlethreads=0 runqueue=0 [0 0 0 0 0 0 0 0 0 0 0 0]
SCHED 1009ms: gomaxprocs=12 idleprocs=12 threads=7 spinningthreads=0 idlethreads=3 runqueue=0 [0 0 0 0 0 0 0 0 0 0 0 0]
SCHED 2013ms: gomaxprocs=12 idleprocs=12 threads=7 spinningthreads=0 idlethreads=3 runqueue=0 [0 0 0 0 0 0 0 0 0 0 0 0]
SCHED 3019ms: gomaxprocs=12 idleprocs=12 threads=7 spinningthreads=0 idlethreads=3 runqueue=0 [0 0 0 0 0 0 0 0 0 0 0 0]
SCHED 4026ms: gomaxprocs=12 idleprocs=12 threads=7 spinningthreads=0 idlethreads=3 runqueue=0 [0 0 0 0 0 0 0 0 0 0 0 0]
SCHED 5033ms: gomaxprocs=12 idleprocs=12 threads=7 spinningthreads=0 idlethreads=3 runqueue=0 [0 0 0 0 0 0 0 0 0 0 0 0]
SCHED 6036ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=21 runqueue=152 [4 1 11 0 2 1 3 10 4 5 1 9]
SCHED 7042ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=19 runqueue=209 [12 7 15 2 18 15 0 13 19 3 2 16]
SCHED 8046ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=19 runqueue=170 [0 12 10 8 4 5 0 3 4 5 1 5]
SCHED 9055ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=21 runqueue=234 [3 0 15 9 3 11 1 10 5 6 10 3]
SCHED 10056ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=19 runqueue=221 [1 13 15 9 5 2 0 0 8 12 4 10]
SCHED 11058ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=18 runqueue=159 [9 12 4 13 4 9 0 10 11 1 1 12]
SCHED 12061ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=19 runqueue=204 [0 16 3 18 7 16 3 2 17 12 18 13]
SCHED 13062ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=1 idlethreads=23 runqueue=237 [16 12 3 13 7 6 0 1 1 15 9 21]
SCHED 14062ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=17 runqueue=201 [16 1 1 11 14 2 5 10 2 0 0 16]
SCHED 15072ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=22 runqueue=26 [0 0 0 0 0 0 0 0 0 0 0 0]
SCHED 16080ms: gomaxprocs=12 idleprocs=0 threads=38 spinningthreads=0 idlethreads=21 runqueue=202 [10 16 18 0 12 0 19 13 9 17 0 1]
SCHED 17087ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=15 runqueue=167 [8 3 12 14 14 14 4 17 1 6 15 6]
SCHED 18092ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=22 runqueue=209 [18 4 0 17 4 9 0 10 0 3 5 16]
SCHED 19093ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=21 runqueue=198 [16 17 3 6 4 1 14 6 18 18 10 21]
SCHED 20093ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=24 runqueue=232 [7 12 13 5 3 7 5 2 8 1 9 8]
SCHED 21100ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=24 runqueue=206 [16 1 14 9 16 7 16 13 5 1 8 4]
SCHED 22110ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=23 runqueue=249 [8 6 8 3 10 9 12 8 6 11 0 3]
SCHED 23113ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=20 runqueue=223 [0 5 17 2 5 3 0 0 0 14 9 2]
SCHED 24116ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=1 idlethreads=22 runqueue=219 [1 0 9 3 1 2 13 10 1 1 18 12]
SCHED 25124ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=23 runqueue=199 [14 12 15 6 2 1 5 0 11 1 15 0]
SCHED 26126ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=21 runqueue=169 [1 12 6 11 8 5 8 5 5 10 6 4]
SCHED 27126ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=20 runqueue=232 [4 5 9 0 1 1 1 1 5 1 11 4]
SCHED 28129ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=21 runqueue=243 [9 11 7 3 7 14 3 12 2 12 12 1]
SCHED 29136ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=20 runqueue=58 [0 4 0 1 0 0 2 0 0 3 0 0]
SCHED 30144ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=22 runqueue=153 [0 9 7 1 4 2 10 2 5 0 0 1]
SCHED 31151ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=22 runqueue=216 [10 4 9 5 14 7 1 10 7 18 8 5]
SCHED 32159ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=24 runqueue=203 [5 8 2 0 5 1 1 14 3 0 14 12]
SCHED 33164ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=22 runqueue=212 [14 13 5 19 0 13 1 8 15 9 11 2]
SCHED 34174ms: gomaxprocs=12 idleprocs=0 threads=40 spinningthreads=0 idlethreads=23 runqueue=266 [6 4 12 12 1 0 8 4 0 3 0 3]
- sched:每一行都代表调度器的调试信息,后面提示的毫秒数表示启动到现在的运行时间,输出的时间间隔受 schedtrace 的值影响。
- gomaxprocs:当前的 CPU 核心数(GOMAXPROCS 的当前值)。
- idleprocs:空闲的处理器数量,后面的数字表示当前的空闲数量。
- threads:OS 线程数量,后面的数字表示当前正在运行的线程数量。
- spinningthreads:自旋状态的 OS 线程数量。
- idlethreads:空闲的线程数量。
- runqueue:全局队列中中的 Goroutine 数量,而后面的 [0 0 1 1] 则分别代表这 4 个 P 的本地队列正在运行的 Goroutine 数量。
参考:
测试相关
TestMain
在写测试时,有时需要在测试之前或之后进行额外的设置(setup)或拆卸(teardown);有时,测试还需要控制在主线程上运行的代码。为了支持这些需求,testing 提供了 TestMain 函数:
func TestMain(m *testing.M)
package mytestmain import ( "flag" "fmt" "os" "testing" ) var db struct { Dns string } func TestMain(m *testing.M) { db.Dns = os.Getenv("DATABASE_DNS") if db.Dns == "" { db.Dns = "root:123456@tcp(localhost:3306)/?charset=utf8&parseTime=True&loc=Local" } flag.Parse() exitCode := m.Run() db.Dns = "" // 退出 os.Exit(exitCode) } func TestDatabase(t *testing.T) { fmt.Println(db.Dns) }
参数化测试
func TestAdd(t *testing.T) { tests := []struct{ name string first int64 second int64 expected int64 } { { name: "HappyPath": first: 2, second: 3, expected: 5, }, { name: "NegativeNumber": first: -1, second: -1, expected: -2, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { assert.Equal(t, test.expected, Add(test.first, test.second)) }) } }
测试集
import ( "testing" "github.com/stretchr/testify/suite" ) type ExampleTestSuite struct { suite.Suite VariableThatShouldStartAtFive int } func (suite *ExampleTestSuite) SetupTest() { suite.VariableThatShouldStartAtFive = 5 } func (suite *ExampleTestSuite) TestExample() { suite.Equal(suite.VariableThatShouldStartAtFive, 5) } func TestExampleTestSuite(t *testing.T) { suite.Run(t, new(ExampleTestSuite)) }
BDD
var _ = Describe("Book", func() { var ( book Book err error ) BeforeEach(func() { book, err = NewBookFromJSON(`{ "title":"Les Miserables", "author":"Victor Hugo", "pages":1488 }`) }) Describe("loading from JSON", func() { Context("when the JSON fails to parse", func() { BeforeEach(func() { book, err = NewBookFromJSON(`{ "title":"Les Miserables", "author":"Victor Hugo", "pages":1488oops }`) }) It("should return the zero-value for the book", func() { Expect(book).To(BeZero()) }) It("should error", func() { Expect(err).To(HaveOccurred()) }) }) }) })
Mock
使用 gomock 提供的 mockgen 工具命令
使用 sqlmock 来模拟数据库的连接
httpmock 就是一个用于 Mock 所有 HTTP 依赖的包,它使用模式匹配的方式匹配 HTTP 请求的 URL,在匹配到特定的请求时就会返回预先设置好的响应。
猴子补丁其实就是一个大杀器了,bouk/monkey 能够通过替换函数指针的方式修改任意函数的实现,所以如果上述的几种方法都不能满足我们的需求,我们就只能够通过猴子补丁这种比较 hack 的方法 Mock 依赖了,:
func main() { monkey.Patch(fmt.Println, func(a ...interface{}) (n int, err error) { s := make([]interface{}, len(a)) for i, v := range a { s[i] = strings.Replace(fmt.Sprint(v), "hell", "*bleep*", -1) } return fmt.Fprintln(os.Stdout, s...) }) fmt.Println("what the hell?") // what the *bleep*? }
不要在单元测试之外的地方使用猴子补丁,我们应该只在必要的时候使用这种方法,例如依赖的第三方库没有提供 interface 或者修改 time.Now 以及 rand.Int63n 等内置函数的返回值用于测试时。
从理论上来说,通过猴子补丁这种方式我们能够在运行时 Mock Go 语言中的一切函数,这也为我们提供了单元测试 Mock 依赖的最终解决方案。
go module upgrades dependencies:
go get -u
(without any arguments) now only upgrades the direct and indirect dependencies of your current package, and no longer examines your entire module.go get -u ./...
from your module root upgrades all the direct and indirect dependencies of your module, and now excludes test dependencies.go get -u -t ./...
is similar, but also upgrades test dependencies.
Go Profiling and Optimization.pptx
一文读懂 Go profiling 和性能优化
GO语言的历史,非常好的演讲,值得一看。
编程语言发展的四波浪潮:
- 第一波浪潮:语言扩张 - 巴别塔
特征:多样化。很久以前,语言是多种多样的,并在在思想、方法和意见等方面体现出多样性。
2 第二波浪潮:语言的标准化
特征:快速、复杂且对开发不友好。语言的标准化发生了数十年。到2000年代,事情开始停滞。他们融合为两个阵营:Java/JVM和C/CLR。C++、Java、C#都非常相似。 - 第三波浪潮:脚本语言
特征:慢、不安全但对开发友好。脚本语言作为对上述语言的复杂性和痛苦的回应而应运而生。它们开发快速而松散,对开发人员友好,但缺乏性能和安全性。 - 第四波浪潮:恢复
特征:快速、安全、对开发人员友好
Go 恢复了早期语言的简单性和灵活性,增加了现代语言的安全性和开发友好性。Go以一种非常真实的方式复兴了许多伟大的想法,这些想法终于准备就绪。
Go 给人的感觉就像是来自60年代,70年代,80年代,90年代,00s,10年代的语言……Steve Francia 2019
Go 的设计哲学
原则1:进化不是革命(Evolution not revolution), 大多数思想都来自先前的思想
大多数思想根本不是新事物
进化不是革命:新语言应该巩固而不是发明新特性
原则2:等待良好的设计, No是暂时的,Yes是永远的(Waiting for Good design No is temporary, Yes is forever )。
在Go的整个历史中,有很多这样的实例。通常的想法是,在设计语言时,不会出现“撤消(undo)”的情况。如果您今天说“No”,那么您明天总是可以说“Yes”,但是如果今天您说“Yes”,那么您将在很长一段时间或永远被它“困”住…。
如有疑问,请将其排除在外。- Joshua Bloch:关于设计的对话– 2002
原则3: 应该使一切都尽可能简单,但不要过于简单(Consensus driven design Everything should be made as simple as possible, but no simpler.)。-爱因斯坦
当我们三个人开始时,这纯粹是研究。…我们从一个想法开始,即我们三个人都必须针对该语言的每个特性进行讨论,因此,无论出于何种原因,都不会在该语言中放入多余的垃圾。- 肯·汤普森(Ken Thompson)访谈– 2011年,肯从Bell Labs学习了这种做法
有两种构建软件设计的方法。一种方法是使其变得如此简单,以至于显然没有缺陷。另一种方法是使其变得如此复杂,以至于没有明显的缺陷。- 托尼·霍尔(Tony Hoare)皇帝的旧衣服-1981年,Go采取了第一种方法,而大多数其他语言都采用第二种方法。
快速迭代期待(Rapid iteration)并实现大规模改变 最后一个原则是快速迭代的原则。
当您处于语言的设计阶段时,您将需要进行频繁且有时是巨大的更改。朝着这个期望前进,并围绕它建立您的流程。
我的看法:明天将要发布的代码选择 Go,在未来五年内保持不变的代码选择 Rust。—Grzegorz Nosek
如果你想加快开发速度,也许是因为你要编写许多不同的服务,或者你有庞大的开发团队,那么 Go 是你选择的语言。Go 为你提供了一流的并发性,并且不容许不安全的内存访问(Rust 也不容忍),但不会强迫你管理每个最后的细节。Go 是快速而强大的工具,但是它避免了使开发人员陷入困境,而专注于简单性和统一性。另一方面,如果需要拧紧块性能,那么 Rust 应该是你的选择。—Andrew Lader[23]
来自 也许是最客观、全面的比较 Rust 与 Go:都想把 Rust 也学一下,原文链接
Find your leaf packages loov/goda:GoDependencyAnalysistoolkit
go get github.com/loov/goda
goda graph ./... | dot -Tsvg -o graph.svg
Golang 枚举生成工具
go get github.com/alvaroloes/enumer
enumer -type=Pill -json -transform=snake
- stringer的用法参考, only generates String() methods, leaving MarshalText() and UnmarshalText() unimplemented.
有一种未经证实的说法:
Go 诞生于 C++ 程序的漫长构建过程中。
如果C++编译很快,那么Robert Griesemer、Rob Pike和Ken Thompson这三位大佬也没有闲暇时间一起喝着咖啡并决定是时候设计一门新语言了。的确,Go语言诞生后,其简洁的语法、极速地构建、新颖的并发结构、体验优良的工具链以及完成度不低的标准库吸引了很多C/C++程序员转型成为Gopher并开始重度使用Go
- https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html
- https://mikespook.com/2012/06/翻译少是指数级的多/
回到 2007 年 9 月,我在一个巨大的 Google C++ 程序(就是你们都用过的那个)上做一些琐碎但是很核心的工作,我在那个巨大的分布式集群上需要花大约 45 分钟进行编译。收到一个通知说 Google 雇佣的一对为 C++ 标准化委员会工作的夫妇将会做一场报告。收到一个通知说几个受雇于 Google 的为 C++ 标准化委员会工作的人将会做一场报告。他们将向我们介绍那时还被称作 C++0x(就是现在众所周知的 C++11)中将会有哪些改进。
在长达一个小时的报告中,我们听说了诸如有已经在计划中的 35 个特性之类的事情。事实上有更多,但仅有 35 个特性在报告中进行了描述。当然一些特性很小,但是意义重大,值得在报告中提出。一些非常微妙和难以理解,如左右值引用(rvalue references),还有一些是 C++ 特有的,如可变参数模板(variadic templates),还有一些就是发疯,如用户定义数据标识(user-defined literals)。
这时我问了自己一个问题:C++ 委员会真得相信 C++ 的问题在于没有足够的特性?肯定的说,在另一个 Ron Hardin 的玩笑中,简化语言的成就远远大于添加功能。当然这有点可笑,不过请务必记住这个思路。
Different Thread Models
There are three different threading models we can see Mx1, 1x1, MxN
Go language implemented MxN with three basic primitive entities
- G Structure — A G struct represents a single goroutine, stack, current stack, stack gaurd, pointer to code (initial function), parameters, goID
- P Structure — Abstraction to the processor in Go runtime. It holds the Context to run Go routine.
- M Structure — The M struct is the Go runtime’s representation of an OS thread, global queue of G’s, the G that it is currently running, its own cache, and a handle to the scheduler.
FROM Different Threading Models — Why I Feel Go Threading Is Better
应该有很多人受不了 err 的反复判断,封装各种 err 处理,达到简化的目的,其中一种实现 ErrorFlow Declarative error handling for Go.
func GzipFile(dstFilename string, srcFilename string) (err error) { // defer IfError()... creates and configures // ErrorFlow error handler for this function. // When any of Check* functions encounters non-nil error // it immediately sends error to this handler // unwinding all stacked defers. errWrapper := errf.WrapperFmtErrorw("error compressing file") defer errf.IfError().ReturnFirst().LogIfSuppressed().Apply(errWrapper).ThenAssignTo(&err) errf.CheckCondition(len(dstFilename) == 0, "dst file should be specified") errf.CheckCondition(len(srcFilename) == 0, "src file should be specified") reader := errf.Io.CheckReadCloser(os.Open(srcFilename)) defer errf.With(errWrapper).Log(reader.Close()) writer := errf.Io.CheckWriteCloser(os.Create(dstFilename)) defer errf.Handle().OnAnyErrOrPanic(func() { os.Remove(dstFilename) }) defer errf.CheckErr(writer.Close()) gzipWriter := gzip.NewWriter(writer) defer errf.CheckErr(gzipWriter.Close()) return errf.CheckDiscard(io.Copy(gzipWriter, reader)) }
vendor 打包编译
开发机上
$ cd app $ go mod download -v $ go mod vendor $ cd .. $ tar --exclude .git --exclude .idea -czf app.tar.gz app
编译机上
$ tar zxf app.tar.gz $ cd app $ go build -mod vendor -o app -ldflags=' -w -s '
package main import ( _ "expvar" "net/http" ) func main() { http.ListenAndServe(":8100", nil) }
go install github.com/divan/expvarmon@latest
expvarmon -ports="8100"
Serve embedded filesystem from root path of URL
package main import ( "embed" "io/fs" "log" "net/http" ) //go:embed static var embeddedFS embed.FS func main() { serverRoot, err := fs.Sub(embeddedFS, "static") if err != nil { log.Fatal(err) } http.Handle("/", http.FileServer(http.FS(serverRoot))) log.Fatal(http.ListenAndServe(":8080", nil)) }
go 可执行文件分析工具 redress
The redress software is a tool for analyzing stripped Go binaries compiled with the Go compiler. It extracts data from the binary and uses it to reconstruct symbols and performs analysis. It essentially tries to "re-dress" a "stripped" binary.
[2021-05-05 22:16:16.837] ❯ redress -help Usage of redress: -compiler Print information -filepath Include file path for packages -force-version string Forcing and using the given version when analyzing -interface Print interfaces -method Print type's methods -pkg List packages -src Print source tree -std Include standard library packages -struct Print structs -type Print all type information -unknown Include unknown packages -vendor Include vendor packages -version Print redress version
[2021-05-05 22:15:51.625] ❯ go install -ldflags="-s -w" ./... [2021-05-05 22:16:01.530] ❯ ls -lh ~/go/bin/shorturl -rwxr-xr-x 1 bingoo staff 15M 5 5 22:16 /Users/bingoo/go/bin/shorturl
[2021-05-05 22:15:22.711] ❯ redress -src ~/go/bin/shorturl Package main: /Users/bingoo/GitHub/shorturl File: main.go main Lines: 12 to 22 (10) Package github.com/bingoohuang/shorturl/pkg: /Users/bingoo/GitHub/shorturl/pkg File: <autogenerated> (*noCache)DeactivateURL Lines: 1 to 1 (0) (*noCache)SavePopularURL Lines: 1 to 1 (0) (*RedisCache)DeactivateURL Lines: 1 to 1 (0) (*noCache)LookupURL Lines: 1 to 1 (0) (*RedisCache)SavePopularURL Lines: 1 to 1 (0) (*Body)Merge Lines: 1 to 1 (0) (*URLInput)ValidateExpiry Lines: 1 to 1 (0) (*RedisCache)LookupURL Lines: 1 to 1 (0) (*URL)IsActive Lines: 1 to 1 (0) (*URLInput)GetExpiresOn Lines: 1 to 1 (0) File: admin.go ListURLsFilteredFromRequest Lines: 9 to 21 (12) ListURLsFiltered Lines: 21 to 46 (25) DeleteURLFromRequest Lines: 46 to 53 (7) DeleteURLByShortCode Lines: 53 to 65 (12) File: client.go CreateURLShortCodeFromRequest Lines: 13 to 25 (12) CreateURLShortCode Lines: 25 to 55 (30) LookupOriginURL Lines: 55 to 76 (21) IncrementHits Lines: 76 to 87 (11) ValidateURLInput Lines: 87 to 115 (28) getUniqueShortCode Lines: 115 to 133 (18) isShortCodeAvail Lines: 133 to 143 (10) getShortCodeByOriginURL Lines: 143 to 153 (10) mapKeywords Lines: 153 to 162 (9) File: config.go init Lines: 28 to 28 (0) createDB Lines: 49 to 74 (25) init0 Lines: 74 to 90 (16) init.0func1 Lines: 75 to 79 (4) createEnvName Lines: 90 to 100 (10) File: controller.go ListURLs Lines: 10 to 22 (12) DeleteShortURL Lines: 22 to 33 (11) CreateShortURL Lines: 33 to 47 (14) NotFound Lines: 47 to 53 (6) ServeShortURL Lines: 53 to 68 (15) File: model.go URLIsActive Lines: 55 to 100 (45) (*URLInput)Validate Lines: 100 to 142 (42) URLInputValidateExpiry Lines: 142 to 164 (22) URLInputGetExpiresOn Lines: 164 to 173 (9) URLFilterGetOffset Lines: 173 to 183 (10) File: redis.go noCacheLookupURL Lines: 19 to 20 (1) noCacheDeactivateURL Lines: 20 to 21 (1) noCacheSavePopularURL Lines: 21 to 29 (8) CreateRedisCache Lines: 29 to 58 (29) CreateRedisCachefunc1 Lines: 36 to 37 (1) RedisCacheLookupURL Lines: 58 to 110 (52) RedisCacheDeactivateURL Lines: 82 to 93 (11) RedisCacheSavePopularURL Lines: 93 to 118 (25) hasURL Lines: 104 to 110 (6) File: router.go init1 Lines: 15 to 22 (7) AssetsRewrite Lines: 22 to 44 (22) AssetsRewritefunc1 Lines: 23 to 67 (44) glob.func1 Lines: 34 to 36 (2) locateHandler Lines: 44 to 66 (22) RegisterHandlers Lines: 66 to 81 (15) RegisterHandlersfunc1 Lines: 67 to 83 (16) Recover Lines: 81 to 94 (13) Recoverfunc1 Lines: 82 to 95 (13) Recover.func11 Lines: 83 to 85 (2) AdminAuth Lines: 94 to 107 (13) AdminAuthfunc1 Lines: 95 to 102 (7) validateAdminToken Lines: 107 to 122 (15) File: util.go RandomString Lines: 17 to 33 (16) BodyMerge Lines: 33 to 43 (10) JSON Lines: 43 to 49 (6)
延长变量的生命周期 runtime.KeepAlive(v)
2019年的一篇文章:https://medium.com/a-journey-with-go/go-keeping-a-variable-alive-c28e3633673a
package main import ( "io/ioutil" "log" "os" "runtime" "syscall" ) type File struct{ d int } func main() { file, err := ioutil.TempFile("", "keepalive") if err != nil { log.Fatal(err) } file.Write([]byte("keepalive")) file.Close() defer os.Remove(file.Name()) p := openFile(file.Name()) content := readFile(p.d) // Ensure p is not finalized until Read returns // runtime.KeepAlive(p) println("Here is the content: " + content) } func openFile(path string) *File { d, err := syscall.Open(path, syscall.O_RDONLY, 0) if err != nil { panic(err) } p := &File{d} runtime.SetFinalizer(p, func(p *File) { syscall.Close(p.d) }) return p } func readFile(descriptor int) string { doSomeAllocation() var buf [1000]byte _, err := syscall.Read(descriptor, buf[:]) if err != nil { panic(err) } return string(buf[:]) } func doSomeAllocation() { var a *int // memory increase to force the GC for i := 0; i < 10000000; i++ { i := 1 a = &i } _ = a }
输出:
panic: device not configured goroutine 1 [running]: main.readFile(0x3, 0x43, 0xc000120010) /Users/bingoo/GitHub/gogotcha/cmd/keepalive/main.go:51 +0x138 main.main() /Users/bingoo/GitHub/gogotcha/cmd/keepalive/main.go:23 +0x176
加上runtime.KeepAlive
后(放开对应的注释行),
Here is the content: keepalive
用组合实现继承
继承,直达问题的本质,清晰易懂
type Foo struct { Base // 继承 ... }
type Foo struct { *Base // 虚拟继承 ... }
来源,许式伟, Go vs. GoPlus(Go+) 2021-6-27 北京 GopherChina2021
介绍了 go 中dig和wire两个DI工具。其中dig是通过运行时反射实现的依赖注入。 而wire是根据自定义的代码,通过命令,生成相应的依赖注入代码,在编译期就完成依赖注入,无需反射机制。 这样的好处是:
- 方便排查,如果存在依赖错误,编译时就能发现。而 dig 只能在运行时才能发现依赖错误。
- 避免依赖膨胀,wire生成的代码只包含被依赖的,而dig可能会存在好多无用依赖。
- 依赖关系静态存在源码,便于工具分析。
为什么腾讯越来越倾向于使用Go语言?
原因可能会有很多,关于Go语言的特性、优势等。但是最主要的原因,应该是基于两方面的考虑:执行性能&开发效率。
Go 语言以其接近 C 的执行性能和近解释型语言的开发效率,以及近乎于完美的编译速度,已经广泛应用于人工智能、云计算开发、容器虚拟化、大数据开发、 数据分析及科学计算、运维开发、爬虫、游戏开发等领域。
Go语言适用于各个领域行业-前景广阔
Sealed Interfaces 密封接口
看到一种 go 代码,接口里定义一个私有无参无返回值的方法,感觉是一种惯用法,原来是密封接口,保证这个接口不给外部去实现。
// Statement represents a statement. type Statement interface { iStatement() SQLNode } func (*Union) iStatement() {} func (*Select) iStatement() {} func (*Insert) iStatement() {} func (*Update) iStatement() {} func (*Delete) iStatement() {} func (*Set) iStatement() {} func (*DDL) iStatement() {} func (*Show) iStatement() {} func (*Use) iStatement() {} func (*OtherRead) iStatement() {} func (*OtherAdmin) iStatement() {} func (*TruncateTable) iStatement() {}
同样的
// SelectStatement any SELECT statement. type SelectStatement interface { iSelectStatement() iStatement() iInsertRows() AddOrder(*Order) SetLimit(*Limit) SQLNode } func (*Select) iSelectStatement() {} func (*Union) iSelectStatement() {} func (*ParenSelect) iSelectStatement() {}
所有模型都是错误的,但有些模型是有用的
在 go-profiler-notes 上看到一句话: All models are wrong
本质上,所有模型都是错误的,但有些模型是有用的。
--- Box,乔治·EP;Norman R.Draper(1987)。经验模型的建立和响应面,p。424,威利。ISBN 0471810339。
我认为最好通过两部分来分析它的含义:
“所有模型都是错误的”,也就是说,每个模型都是错误的,因为它是对现实的简化。一些模型,特别是在“硬”科学中,只是有点错误。他们无视摩擦或微小物体的引力作用。其他模型有很多错误-他们忽略了更大的事情。在社会科学中,我们忽略了很多。
“但是有些有用”-简化现实可能非常有用。它们可以帮助我们解释,预测和理解宇宙及其所有组成部分。
这不仅在统计上是正确的!地图是一种模型。他们错了。但是好的地图非常有用。其他有用但错误的模型的例子比比皆是。
比例为1:1的完美地图的幻想已被许多作者使用,包括Lewis Carroll,Jorge Luis Borges和Umberto Eco。实际上,这没有用,因为它所映射的区域必然很复杂,而且不容易理解(更不用说将其展开并布置为阅读的尴尬了)。
也许您还可以补充一点,就是模型必须有点错误,因为否则它将无法泛化,因此无法在其他地方应用。有一些答案说明了这一点。但是现在有太多的答案无法全部阅读。
对我来说,真正的见解在于以下方面:
模型不一定非要正确才有用。
不幸的是,在许多科学中,人们常常忘记了模型不一定必须是现实的精确表示即可允许新的发现和预测!
因此,不要浪费您的时间来构建需要对无数变量进行准确测量的复杂模型。真正的天才发明了可以完成这项工作的简单模型。
unsafe.Pointer and uintptr
package main import "unsafe" func f() { var p uintptr for i := range [10]int{} { var x = i if i == 0 { // uintptr类型的临时变量只是一个普通的数字,所以其值不应该被改变。 // 因此,x 变量在栈上,10次循环,所使用的栈地址不变,因此 p 最后的值是9 p = uintptr(unsafe.Pointer(&x)) } } println(*(*int)(unsafe.Pointer(p))) // 9 } func g() { var p unsafe.Pointer for i := range [10]int{} { var x = i if i == 0 { // 从gc视角来看,unsafe.Pointer是一个指向变量的指针,因此当变量被移动是对应的指针也必须被更新 // 为了跟踪指针更新, x 逃逸到了堆上,10次循环,生成了10个不同的x堆变量, p指向了第1个,其值是0 p = unsafe.Pointer(&x) } } println(*(*int)(p)) // 0 } func main() { f() g() }
$ go run -gcflags="-m" p.go # command-line-arguments ./p.go:19:7: moved to heap: x 9 0
从垃圾收集器的视角来看,一个unsafe.Pointer是一个指向变量的指针,因此当变量被移动是对应的指针也必须被更新;但是uintptr类型的临时变量只是一个普通的数字,所以其值不应该被改变。
此例,即考察了 unsafe.Pointer
,uintptr
,又考察了变量逃逸。
go1.18 泛型变快了嘛?
Credit: https://unsplash.com/photos/CpkOjOcXdUY
看博客 Go is about to get a whole lot faster
源代码:
操作步骤:
cd deque-generic && go1.18beta1 test -run=NONE -bench=. > ../generic.txt
cd deque-non-generic && go1.18beta1 test -run=NONE -bench=. > ../none.txt
cd deque-non-generic && go test -run=NONE -bench=. > ../none-go1.17.6.txt
结果输出:
$ go install golang.org/x/tools/cmd/benchcmp@latest deque more generic.txt goos: darwin goarch: amd64 pkg: github.com/sekoyo/deque cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz BenchmarkPushFront-12 97286768 12.28 ns/op BenchmarkPushBack-12 142144150 14.43 ns/op BenchmarkSerial-12 100000000 11.82 ns/op BenchmarkSerialReverse-12 100000000 11.21 ns/op BenchmarkRotate-12 56238 120075 ns/op BenchmarkInsert-12 36441 120364 ns/op BenchmarkRemove-12 107545 119688 ns/op BenchmarkYoyo-12 1777 675390 ns/op BenchmarkYoyoFixed-12 2662 455608 ns/op PASS ok github.com/sekoyo/deque 33.914s deque more non.txt goos: darwin goarch: amd64 pkg: github.com/gammazero/deque cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz BenchmarkPushFront-12 25286799 50.48 ns/op BenchmarkPushBack-12 28099020 41.14 ns/op BenchmarkSerial-12 25631952 50.65 ns/op BenchmarkSerialReverse-12 26357466 50.56 ns/op BenchmarkRotate-12 40188 119641 ns/op BenchmarkInsert-12 28888 120136 ns/op BenchmarkRemove-12 87488 123670 ns/op BenchmarkYoyo-12 574 2039762 ns/op BenchmarkYoyoFixed-12 877 1382135 ns/op PASS ok github.com/gammazero/deque 28.086s deque benchcmp none.txt generic.txt benchcmp is deprecated in favor of benchstat: https://pkg.go.dev/golang.org/x/perf/cmd/benchstat benchmark old ns/op new ns/op delta BenchmarkPushFront-12 50.5 12.3 -75.67% BenchmarkPushBack-12 41.1 14.4 -64.92% BenchmarkSerial-12 50.6 11.8 -76.66% BenchmarkSerialReverse-12 50.6 11.2 -77.83% BenchmarkRotate-12 119641 120075 +0.36% BenchmarkInsert-12 120136 120364 +0.19% BenchmarkRemove-12 123670 119688 -3.22% BenchmarkYoyo-12 2039762 675390 -66.89% BenchmarkYoyoFixed-12 1382135 455608 -67.04% deque more none-go1.17.6.txt goos: darwin goarch: amd64 pkg: github.com/gammazero/deque cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz BenchmarkPushFront-12 24497521 50.09 ns/op BenchmarkPushBack-12 32226673 39.35 ns/op BenchmarkSerial-12 28485412 46.10 ns/op BenchmarkSerialReverse-12 27771453 46.76 ns/op BenchmarkRotate-12 40466 121028 ns/op BenchmarkInsert-12 28731 121774 ns/op BenchmarkRemove-12 86724 125651 ns/op BenchmarkYoyo-12 620 2049784 ns/op BenchmarkYoyoFixed-12 820 1389294 ns/op PASS ok github.com/gammazero/deque 28.335s
还真是,在 PushFront 上竟然快了 75%,看来等 1.18 正式发布了,还是要香一下的。
Go 可执行文件大小 树图分析
go install github.com/nikolaydubina/go-binsize-treemap@latest
go tool nm -size <binary finename> | go-binsize-treemap > binsize.svg
在线火焰图
Pyroscope Go Playground
有名返回参数的坑
- https://mp.weixin.qq.com/s/RpeiByFggXal07awqfT8vA
- https://twitter.com/bwplotka/status/1494362886738780165
package main func aaa() (done func(), err error) { return func() { print("aaa: done") }, nil } func bbb() (done func(), _ error) { done, err := aaa() return func() { print("bbb: surprise!") done() }, err } func main() { done, _ := bbb() done() }
这个程序输出结果是什么呢(单选)?
- 输出 aaa: done
- 输出 bbb: surprise!aaa: done
- 一直输出,永远不结束
- 程序最终运行出错
正确答案是:D,程序最终运行出错。一直调用一直爽,直至栈溢出程序崩溃。
他会不断地递归,疯狂输出 “bbb: surprise!”,直至栈溢出,导致程序运行出错,最终中止。
同学就疑惑了,怎么又多出了个递归?
本质上在函数 bbb 执行完毕后, 变量 done 已经变成了一个递归函数。
递归的过程是:函数 bbb 调用变量 done 后,会输出 bbb: surprise! 字符串,然后又调用变量 done。而变量 done 又是这个闭包(匿名函数),从而实现不断递归调用和输出。
总结
这位大佬出的题目,本质上是比较烦人的,其结合了函数返回参数的命名用法。
如果我们把这个函数的返回参数命名去掉,就可以避开这个问题。
详解 Go 中的 rune 类型
https://mp.weixin.qq.com/s/hcrq5fYaQ7FN_2oSMRNjcA
Go 语言把字符分 byte 和 rune 两种类型处理。byte 是类型 unit8 的别名,用于存放占 1 字节的 ASCII 字符,如英文字符,返回的是字符原始字节。rune 是类型 int32 的别名,用于存放多字节字符,如占 3 字节的中文字符,返回的是字符 Unicode 码点值。如下图所示:
s := "Go语言编程" // byte fmt.Println([]byte(s)) // 输出:[71 111 232 175 173 232 168 128 231 188 150 231 168 139] // rune fmt.Println([]rune(s)) // 输出:[71 111 35821 35328 32534 31243]
docker 编译
Dockerfile:
FROM golang:alpine AS builder WORKDIR /build RUN apk add upx COPY . . RUN go build -ldflags "-s -w" -o hello hello.go && upx hello FROM alpine WORKDIR /build COPY --from=builder /build/hello /build/hello CMD ["./hello"]
docker build -t hello:v1 .
dive hello:v1 .
$ docker run -it --rm hello:v1 hello world! $ docker run -it --rm hello:v1 ls -lh /build total 332K -rwxr-xr-x 1 root root 331.2K Mar 15 02:12 hello $ docker images | grep hello hello v1 b3762d8a6c76 12 minutes ago 5.92MB
为什么是 int 类型
package main import ( "fmt" ) func main() { v := 4 fmt.Printf("%T\n", v) f := ((v / 5.0) * 100.0) * 11.0 / 100.0 fmt.Printf("Type of result %T for value %0.2f\n", f, f) fmt.Printf("\n%T\n", 4) f2 := ((4 / 5.0) * 100.0) * 11.0 / 100.0 fmt.Printf("Type of result %T for value %0.2f\n", f2, f2) }
输出结果:
int Type of result int for value %!f(int=00) int Type of result float64 for value 8.80
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 玩转一下 Python
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论