Skip to content

Latest commit

 

History

History
361 lines (310 loc) · 14.4 KB

go.org

File metadata and controls

361 lines (310 loc) · 14.4 KB

go笔记

  1. escape analysis 堆和栈,对于C/C++而言auto变量自然分配在栈上而如果需要分配在堆上则需要通过malloc调用来分配内存在 堆上。而对于Go语言来讲编译时候会根据是否有外部引用来判断是否将内存分配在堆或者是栈上。 Five things that make Go fast这篇文章里有详细的叙述。所以对于有些地方直接将类似与C/C++中的 auto变量地址返回的这样的情景请不要惊讶。编译器已经考虑到了。
  2. Sender close the channel
  3. using recover() trapped the panic
  4. format %T get the type
  5. %q a single-quoted character literal safely escaped with Go syntax.
  6. trace: GOMAXPROCS=2 GODEBUG=schedtrace=1000,scheddetail=1 ./trace https://www.goinggo.net/2015/02/scheduler-tracing-in-go.html for detail

go code fix

github.com\rogpeppe\godef\go\parser\parser.go

// p.error(path.Pos(), fmt.Sprintf(“cannot find identifier for package %q: %v”, litToString(path), err))

fixed this

init vs main?

Programs initialize and begin execution from the main package. If the main package imports other packages, they will be imported in the compile time. If one package is imported many times, it will be only compiled once. After importing packages, programs will initialize the constants and variables within the imported packages, then execute the init function if it exists, and so on. After all the other packages are initialized, programs will initialize constants and variables in the main package, then execute the init function inside the package if it exists. The following figure shows the process.

dot import

import(
    . "fmt"
)

The dot operator means you can omit the package name when you call functions inside of that package. Now `fmt.Printf(“Hello world”)` becomes to `Printf(“Hello world”)`.

Assertion of Comma-ok pattern

if value, ok := element.(int); ok

why golang’s default ServeMux match ”notfound” path to ”

the answer is in the pathMatch function.

rob pike

Go is more about software engineering than programming language research. Or to rephrase, it is about language design in the service of software engineering.

make vs new?

Go有两个数据结构创建函数:new和make。两者的区别在学习Go语言的初期是一个常见的混淆点。基本的区别是`new(T)`返回一个`*T`,返回的这个指针可以被隐式地消除引用(图中的黑色箭头)。而`make(T, args)`返回一个普通的T。通常情况下,T内部有一些隐式的指针(图中的灰色箭头)。一句话,new返回一个指向已清零内存的指针,而make返回一个复杂的结构。

continious stack

方便goroutine的stack

网络api netpoll

On Linux, it uses epoll, on the BSDs and Darwin, it uses kqueue and on Windows it uses IoCompletionPort.

build option

-gcflags=’-N -l’ 否则local变量会被优化掉不好调试。 1.10以后需要调整为: -gcflags=”all=-N -l”

package命令规则

是单数,不是复数。

http gzip response write

https://gist.github.com/the42/1956518

pprof[fn:1]:

package main

import (
  "fmt"
  "log"
  "net/http"
  _ "net/http/pprof" // here be dragons
)

func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World!")
  })
  log.Fatal(http.ListenAndServe(":8080", nil))
}

url:

  • /debug/pprof/profile: 30-second CPU profile
  • /debug/pprof/heap: heap profile
  • /debug/pprof/goroutine?debug=1: all goroutines with stack traces
  • /debug/pprof/trace: take a trace

http response body process:

Body不关闭,http客户端无法复用tcp连接,导致连接数上涨。需要做到两点:

res, _ := client.Do(req)
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()

即body应当被读取,然后关闭。

gorm memleak:

gorm need rows.Close()

ps: how to check gorouine leak? https://github.com/fortytw2/leaktest runtime.Stack然后选取感兴趣的即可

interface in struct

https://stackoverflow.com/questions/24537443/meaning-of-a-struct-with-embedded-anonymous-interface

Which declaration form should I use?

Use long declaration when you can’t know what data to store beforehand, otherwise, use short declaration. Use multiple declaration when you want to define multiple variables together or as an hint for code readability that the variables will be used together.

You can’t use short declarations outside of functions including main function. Or: you will meet with this error: “syntax error: non-declaration statement outside function body”.

add line number for a log:

log.SetFlags(log.LstdFlags | log.Lshortfile)

关于GMP之间的关系以及为什么引入P

许多关于Go调度的上来就是给你GMP,但这里的展开我觉的讲的很好 https://news.ycombinator.com/item?id=12459841 这个也不错, http://morsmachine.dk/go-scheduler

go lang tool pprof

tl,dr : [fn:2]

go get github.com/google/pprof
import (
"runtime/pprof"
)
pprof.StartCPUProfile(os.Stdout)
defer pprof.StopCPUProfile()

生成profile文件之后用tool来inspect:

go tool pprof cpu.profile

内存全量使用情况: go tool pprof -alloc_space http://localhost:6060/debug/pprof/heap 当前情况使用: go tool pprof -inuse_space http://localhost:6060/debug/pprof/heap 保存png文件: apt-get install graphviz go tool pprof -png http://localhost:6060/debug/pprof/heap > data.png 如果是想知道CPU哪里慢了,用pprof 看看goroutine情况的URL http://localhost:6060/debug/pprof/goroutine?debug=1 跟着pprof的一个golang命令行工具: https://github.com/google/gops

create reader from string?

strings.NewReader(s)

ssh connect方法:

github.com/rapidloop/rtop 这个repo里有相关方法,可以参考

coredump的查看

还是用dlv工具[fn:3]

go tool的一些参数

  1. go build -x 看看过程都干了啥
  2. go test -race 死锁检测
  3. go get -d 只是clone不去安装
  4. 重置test时间[fn:4] b.ResetTimer() or: b.StopTimer() at some point then b.StartTimer() just brefore the bench you want
  5. 显示分配内存情况 b.ReportAllocs()
  6. gen profile -cpuprofile=$FILE writes a CPU profile to $FILE. -memprofile=$FILE, writes a memory profile to $FILE, -memprofilerate=N adjusts the profile rate to 1/N. -blockprofile=$FILE, writes a block profile to $FILE. Using any of these flags also preserves the binary. % go test -run=XXX -bench=. -cpuprofile=c.p bytes % go tool pprof c.p % go test -gcflags=-N -bench=. #禁止编译器优化

grpc

编译proto: protoc –go_out=plugins=grpc:. helloworld.proto 定义的proto会生成对应Client/Server的interface, 后面对应server的实现函数signature就需要和interface中指定的一样。 主要是stream这里需要理解一下,可以单向也可以全双工。通过获得stream然后对应读和写操作。

防止goroutine泄露

doWork := func(done <-chan interface{}, strchan <-chan string) <-chan interface{} {
  terminated := make(chan interface{})
  go func() {
    defer fmt.Println("doWork exited.")
    defer close(terminated)
    for {
      select {
      case s := <-strchan:
        fmt.Println(s)
      // add this to prevent goroutine leak
      case <-done:
        return
      }
    }
  }()
  return terminated
}

一般都会添加一个done channel用来退出,不妨称为done channel pattern吧。

fetch go value type?

reflect.TypeOf(s) reflect.ValueOf(s).Kind() fmt.Printf(“%T\n”, s)

interface internel

go help environment

环境变量都在这里了

print stack trace of a hung process

To kill a hung Go process and print the current stacks of all goroutines, simply send it an ABRT signal:

kill -ABRT <pid>[fn:5]

gitaly profile

https://gitlab.com/gitlab-com/runbooks/blob/master/howto/gitaly-profiling.md

to marshal arbitrary JSON you can marshal to map[string]interface{}{}

sql

  • Statements that don’t return rows should not use Query functions; they should use Exec().
  • Although it’s idiomatic to Close() the database when you’re finished with it, the sql.DB object is designed to be long-lived. Don’t Open() and Close() databases frequently. Instead, create one sql.DB object for each distinct datastore you need to access, and keep it until the program is done accessing that datastore.
  • prepares, executes, and closes 三部曲
  • This also means that prepared statements created inside a Tx can’t be used separately from it. Likewise, prepared statements created on a DB can’t be used within a transaction, because they will be bound to a different connection. placeholder:
MySQLPostgreSQLOracle
WHERE col = ?WHERE col = $1WHERE col = :col
VALUES(?, ?, ?)VALUES($1, $2, $3)VALUES(:val1, :val2, :val3)
for rows.Next() {
    // ...
}
// always check error
if err = rows.Err(); err != nil {
    // handle the error here
}


// You should always explicitly close a sql.Rows if you exit the loop prematurely, as previously mentioned. It’s auto-closed if the loop exits normally or through an error, but you might mistakenly do this:
for rows.Next() {
  // ...
  break; // whoops, rows is not closed! memory leak...
}
// do the usual "if err = rows.Err()" [omitted here]...
// it's always safe to [re?]close here:
if err = rows.Close(); err != nil {
  // but what should we do if there's an error?
  log.Println(err)
}

怎样看 p g m ? go调度的抽象基本单元:

把P看成是一个context,m必须获得p然后才能运行G。: https://news.ycombinator.com/item?id=12460807 这个解释非常到位,一看作者是golang的maintaner,难怪

return and defer

A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns.

关于内存

It is not possible to create a Go program where two variables share the same storage location in memory. It is possible to create two variables whose contents point to the same storage location, but that is not the same thing as two variables who share the same storage location.

package main

import "fmt"

func main() {
        var a int
        var b, c = &a, &a
        fmt.Println(b, c)   // 0x1040a124 0x1040a124
        fmt.Println(&b, &c) // 0x1040c108 0x1040c110
}

If a map isn’t a reference variable, what is it?

==> A map value is a pointer to a runtime.hmap structure.[fn:6]

init执行放在哪里[fn:2]

知道是在main前面但是和变量初始化比呢?顺序如下:

  1. If a package imports other packages, the imported packages are initialised first.
  2. Current package’s constant initialized then.
  3. Current package’s variables are initialiszd then.
  4. Finally, init() function of current package is called.

传参法则[fn:7]

  1. Methods using receiver pointers are common; the rule of thumb for receivers is, “If in doubt, use a pointer.”
  2. Slices, maps, channels, strings, function values, and interface values are implemented with pointers internally, and a pointer to them is often redundant.
  3. Elsewhere, use pointers for big structs or structs you’ll have to change, and otherwise pass values, because getting things changed by surprise via a pointer is confusing.

获取当前执行目录

1.8以后用 os.Executable 来做。

function receiver

The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers.

iota

iota比较特殊,可以被认为是一个可被编译器修改的常量,在每一个const关键字出现时被重置为0,然后在下一个const出现之前,每出现一次iota,其所代表的数字会自动增1。

golang scheduler

这里[fn:8]讲的不错。

break in select

如下:

for {
  select {
    case xxx:
      break
  }
}

这里的break是select还是for? 答案是select

atomic vs mutex

如果支持atomic的读写就用这个,因为这个更快一点

发现shadow变量

go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow go vet -vettool=$(which shadow)

repo[fn:9]的一些记录

golang gc 测试 go tool pprof -alloc_objects

go tool pprof -inuse_objects

go tool pprof bin/dupsdc

consistant hash Ketema algorithm

goreplay: https://github.com/buger/goreplay 用线上流量去压测 begeta: https://github.com/tsenart/vegeta http压测 gogo/protobuf: use in docker/etcd/k8s/tidb 压测数据 https://github.com/alecthomas/go_serialization_benchmarks runtime.MemoProfile() memory profile golang 的价值观: orthogonal composition of simple concepts with preference in concurrency go-kit/gokit: 微服务的基础框架 go tool compile -m 辅助分析对象的分配情况 “The go tool will ignore a directory named “testdata”, making it available to hold ancillary data needed by the tests.” go test -v -run=’^TestContainsAny$’ strings_test.go 之前许多时候对prefix相同的testcase没有办法,其实可以用正则。 云架构的核心就是解耦: 业务/运维解耦 系统/软件解耦 服务与服务之间解耦

copy

// The number of elements copied by the copy function is the minimum of len(dst) and len(src). To make a full copy, you must allocate a big enough destination slice.
var src, dst []int
src = []int{1, 2, 3}
dst = make([]int, len(src))
n := copy(dst, src)
fmt.Println("dst:", dst, "(copied", n, "numbers)")

Footnotes

[fn:1] http://mmcloughlin.com/posts/your-pprof-is-showing

[fn:2] http://www.integralist.co.uk/posts/profiling-go/

[fn:3] https://rakyll.org/coredumps/

[fn:4] https://dave.cheney.net/high-performance-go-workshop/gophercon-2019.html

[fn:5] http://pro-tips-dot-com.tumblr.com/post/47677612115/kill-a-hung-go-process-and-print-stack-traces

[fn:6] https://dave.cheney.net/2017/04/30/if-a-map-isnt-a-reference-variable-what-is-it

[fn:7] https://i.stack.imgur.com/6S35J.png

[fn:8] https://news.ycombinator.com/item?id=12460807

[fn:9] https://github.com/0voice/from_coder_to_expert