本文永久链接 - https://tonybai.com/2021/06/04/go-source-analysis-with-functrace
在《像跟踪分布式服务调用那样跟踪Go函数调用链》一文中,我们介绍了一种跟踪函数调用链的思路,并给出了一种实现functrace:https://github.com/bigwhite/functrace。这个小工具不仅仅是分享给大家的,我自己在工作和学习时也在使用。最近发现这个小工具在阅读和分析某个Go项目源码时也能起到关键的辅助作用。这里就和大家简单讲解一下如何用functrace来辅助Go源码阅读和分析。
程序员的日常离不开“源码阅读和分析”,日常阅读代码的姿势无非是这么几种(或几种的组合):
无论哪一种方式,最终只要时间够长,态度到位,总是会将代码分析出个七七八八的。
就笔者来看,无论是哪种范式:命令式、面向对象、函数式,最终梳理出来的源码脉络都是建立在执行基本单元(函数或方法)上,代码的执行主线(并发程序会有若干条)本质上就是一条函数/方法调用链。只要把这条链理出来,代码理解起来就不难了。上述的代码阅读方法实质也是参照这个逻辑的。只是对于调用层次较深,还伴随有回调的代码,梳理调用链难度高、效率低。
functrace最初用于跟踪函数调用链(得益于Go核心开发团队公开的抽象语法树AST API),但如果在阅读代码时直接用functrace输出函数调用链,那将大幅提高我们源码阅读分析的效率。下面我们就用一个样例项目来试试如何用functrace梳理出代码的执行主线。
我们以Go高性能、轻量级、非阻塞的事件驱动网络框架gnet为例,来看看如何阅读分析gnet的源码。首先我们需要安装functrace工具:
$go install github.com/bigwhite/functrace/cmd/gen@latest go: downloading github.com/bigwhite/functrace v0.0.0-20210603024853-ccab68a2604c go: downloading golang.org/x/tools v0.0.0-20201204062850-545788942d5f $gen -h [gen -h] gen [-w] xxx.go -w write result to (source) file instead of stdout
接下来,我们下载要进行源码分析的gnet源码:
$git clone git@github.com:panjf2000/gnet.git
我们进入gnet目录,现在我们可以使用gen命令为任意go源文件添加“跟踪设施”了,比如:
$gen -w gnet.go [gen -w gnet.go] add trace for gnet.go ok $ git diff gnet.go diff --git a/gnet.go b/gnet.go index b4c04a5..a7afe2b 100644 --- a/gnet.go +++ b/gnet.go @@ -29,6 +29,7 @@ import ( "sync" "time" + "github.com/bigwhite/functrace" "github.com/panjf2000/gnet/errors" "github.com/panjf2000/gnet/internal" "github.com/panjf2000/gnet/internal/logging" ... ...
我们可以这样根据自己的需要在特定的go源文件上添加“跟踪设施”,但是多数情况下,我们也可以通过脚本为项目内所有go源文件批量添加“跟踪设施”,functrace项目提供了一个简单的脚本batch_add_trace.sh,下面我们就来通过该脚本将gnet下的go源文件批量加上函数跟踪设施:
下载functrace源码:
$git clone https://github.com/bigwhite/functrace.git
将functrace/scripts/batch_add_trace.sh 拷贝到上面gnet目录下并执行下面命令:
# bash batch_add_trace.sh ... ... [gen -w ./server_unix.go] add trace for ./server_unix.go ok [gen -w ./internal/socket/sockopts_posix.go] add trace for ./internal/socket/sockopts_posix.go ok ... ... [gen -w ./ringbuffer/ring_buffer_test.go] add trace for ./ringbuffer/ring_buffer_test.go ok [gen -w ./ringbuffer/ring_buffer.go] add trace for ./ringbuffer/ring_buffer.go ok [gen -w ./pool/bytebuffer/bytebuffer.go] no trace added for ./pool/bytebuffer/bytebuffer.go [gen -w ./pool/goroutine/goroutine.go] add trace for ./pool/goroutine/goroutine.go ok [gen -w ./pool/ringbuffer/ringbuffer.go] add trace for ./pool/ringbuffer/ringbuffer.go ok [gen -w ./loop_linux.go] add trace for ./loop_linux.go ok [gen -w ./server_windows.go] add trace for ./server_windows.go ok
接下来我们编写一个基于gnet的程序,我们就使用gnet参加TechEmpower的那份代码:
//main.go package main import ( "bytes" "flag" "fmt" "log" "runtime" "time" "github.com/panjf2000/gnet" ) type httpServer struct { *gnet.EventServer } type httpCodec struct { delimiter []byte } func (hc *httpCodec) Encode(c gnet.Conn, buf []byte) (out []byte, err error) { return buf, nil } func (hc *httpCodec) Decode(c gnet.Conn) (out []byte, err error) { buf := c.Read() if buf == nil { return } c.ResetBuffer() // process the pipeline var i int pipeline: if i = bytes.Index(buf, hc.delimiter); i != -1 { out = append(out, "HTTP/1.1 200 OK\r\nServer: gnet\r\nContent-Type: text/plain\r\nDate: "...) out = time.Now().AppendFormat(out, "Mon, 02 Jan 2006 15:04:05 GMT") out = append(out, "\r\nContent-Length: 13\r\n\r\nHello, World!"...) buf = buf[i+4:] goto pipeline } // request not ready, yet return } func (hs *httpServer) OnInitComplete(srv gnet.Server) (action gnet.Action) { log.Printf("HTTP server is listening on %s (multi-cores: %t, loops: %d)\n", srv.Addr.String(), srv.Multicore, srv.NumEventLoop) return } func (hs *httpServer) React(frame []byte, c gnet.Conn) (out []byte, action gnet.Action) { // handle the request out = frame return } func init() { runtime.GOMAXPROCS(runtime.NumCPU() * 2) } func main() { var port int var multicore bool // Example command: go run main.go --port 8080 --multicore=true flag.IntVar(&port, "port", 8080, "server port") flag.BoolVar(&multicore, "multicore", true, "multicore") flag.Parse() http := new(httpServer) hc := &httpCodec{delimiter: []byte("\r\n\r\n")} // Start serving! log.Fatal(gnet.Serve(http, fmt.Sprintf("tcp://:%d", port), gnet.WithMulticore(multicore), gnet.WithCodec(hc))) }
构建这份代码:
$go mod init gnet-demo $go get github.com/panjf2000/gnet go: downloading github.com/panjf2000/gnet v1.4.5 go get: added github.com/panjf2000/gnet v1.4.5 //修改go.mod,使用replace让gnet-demo使用本地的gnet代码 $cat go.mod module gnet-demo go 1.16 replace github.com/panjf2000/gnet => /root/go/src/github.com/panjf2000/gnet require ( github.com/panjf2000/gnet v1.4.5 ) $go get github.com/bigwhite/functrace go get: added github.com/bigwhite/functrace v0.0.0-20210603024853-ccab68a2604c $go build -tags trace //-tags trace务必不能省略,这个是开启functrace的关键
构建后,我们来执行构建出的可执行程序:gnet-demo:
$ go build -tags trace root@VM-0-12-ubuntu:~/test/go/gnet-demo# ./gnet-demo g[01]: ->github.com/panjf2000/gnet/internal/socket.maxListenerBacklog g[01]: <-github.com/panjf2000/gnet/internal/socket.maxListenerBacklog g[01]: ->github.com/panjf2000/gnet/ringbuffer.New g[01]: <-github.com/panjf2000/gnet/ringbuffer.New g[01]: ->github.com/panjf2000/gnet/internal/logging.init.0 g[01]: <-github.com/panjf2000/gnet/internal/logging.init.0 g[01]: ->github.com/panjf2000/gnet.WithMulticore g[01]: <-github.com/panjf2000/gnet.WithMulticore g[01]: ->github.com/panjf2000/gnet.WithCodec g[01]: <-github.com/panjf2000/gnet.WithCodec g[01]: ->github.com/panjf2000/gnet.Serve g[01]: ->github.com/panjf2000/gnet.loadOptions g[01]: <-github.com/panjf2000/gnet.loadOptions g[01]: ->github.com/panjf2000/gnet.parseProtoAddr g[01]: <-github.com/panjf2000/gnet.parseProtoAddr g[01]: ->github.com/panjf2000/gnet.initListener g[01]: ->github.com/panjf2000/gnet.(*listener).normalize g[01]: ->github.com/panjf2000/gnet/internal/socket.TCPSocket g[01]: ->github.com/panjf2000/gnet/internal/socket.tcpSocket g[01]: ->github.com/panjf2000/gnet/internal/socket.getTCPSockaddr g[01]: ->github.com/panjf2000/gnet/internal/socket.determineTCPProto g[01]: <-github.com/panjf2000/gnet/internal/socket.determineTCPProto g[01]: <-github.com/panjf2000/gnet/internal/socket.getTCPSockaddr g[01]: ->github.com/panjf2000/gnet/internal/socket.sysSocket g[01]: <-github.com/panjf2000/gnet/internal/socket.sysSocket g[01]: ->github.com/panjf2000/gnet/internal/socket.SetNoDelay g[01]: <-github.com/panjf2000/gnet/internal/socket.SetNoDelay g[01]: <-github.com/panjf2000/gnet/internal/socket.tcpSocket g[01]: <-github.com/panjf2000/gnet/internal/socket.TCPSocket g[01]: <-github.com/panjf2000/gnet.(*listener).normalize g[01]: <-github.com/panjf2000/gnet.initListener g[01]: ->github.com/panjf2000/gnet.serve 2021/06/03 14:53:30 HTTP server is listening on :8080 (multi-cores: true, loops: 1) g[01]: ->github.com/panjf2000/gnet.(*server).start g[01]: ->github.com/panjf2000/gnet.(*server).activateReactors g[01]: ->github.com/panjf2000/gnet/internal/netpoll.OpenPoller g[01]: ->github.com/panjf2000/gnet/internal/netpoll.(*Poller).AddRead g[01]: <-github.com/panjf2000/gnet/internal/netpoll.(*Poller).AddRead g[01]: ->github.com/panjf2000/gnet/internal/netpoll/queue.NewLockFreeQueue g[01]: <-github.com/panjf2000/gnet/internal/netpoll/queue.NewLockFreeQueue g[01]: <-github.com/panjf2000/gnet/internal/netpoll.OpenPoller g[01]: ->github.com/panjf2000/gnet.(*roundRobinLoadBalancer).register g[01]: <-github.com/panjf2000/gnet.(*roundRobinLoadBalancer).register g[01]: ->github.com/panjf2000/gnet.(*server).startSubReactors g[01]: ->github.com/panjf2000/gnet.(*roundRobinLoadBalancer).iterate g[01]: <-github.com/panjf2000/gnet.(*roundRobinLoadBalancer).iterate g[01]: <-github.com/panjf2000/gnet.(*server).startSubReactors g[01]: ->github.com/panjf2000/gnet/internal/netpoll.OpenPoller g[01]: ->github.com/panjf2000/gnet/internal/netpoll.(*Poller).AddRead g[01]: <-github.com/panjf2000/gnet/internal/netpoll.(*Poller).AddRead g[01]: ->github.com/panjf2000/gnet/internal/netpoll/queue.NewLockFreeQueue g[01]: <-github.com/panjf2000/gnet/internal/netpoll/queue.NewLockFreeQueue g[01]: <-github.com/panjf2000/gnet/internal/netpoll.OpenPoller g[01]: ->github.com/panjf2000/gnet/internal/netpoll.(*Poller).AddRead g[01]: <-github.com/panjf2000/gnet/internal/netpoll.(*Poller).AddRead g[01]: <-github.com/panjf2000/gnet.(*server).activateReactors g[01]: <-github.com/panjf2000/gnet.(*server).start g[01]: ->github.com/panjf2000/gnet.(*server).stop g[01]: ->github.com/panjf2000/gnet.(*server).waitForShutdown g[07]: ->github.com/panjf2000/gnet.(*server).activateMainReactor g[07]: ->github.com/panjf2000/gnet/internal/netpoll.(*Poller).Polling g[07]: ->github.com/panjf2000/gnet/internal/netpoll.newEventList g[07]: <-github.com/panjf2000/gnet/internal/netpoll.newEventList g[06]: ->github.com/panjf2000/gnet.(*server).activateSubReactor g[06]: ->github.com/panjf2000/gnet/internal/netpoll.(*Poller).Polling g[06]: ->github.com/panjf2000/gnet/internal/netpoll.newEventList g[06]: <-github.com/panjf2000/gnet/internal/netpoll.newEventList
我们看到gnet的执行主线被清晰的打印出来,通过输出的函数所在包我们可以轻松找到对应的源文件。g[01]这goroutine显然是main goroutine,整个程序的初始化线索通过跟踪g[01]的函数链便一目了然。
如果我们要看gnet是如何处理一个外部链接的,我们可以向gnet-demo建立一个连接,看看gnet-demo的输出。
我们通过curl命令向gnet-demo发起一个http请求:
$curl localhost:8080 Hello, World!
gnet-demo输出:
g[07]: ->github.com/panjf2000/gnet.(*server).acceptNewConnection g[07]: ->github.com/panjf2000/gnet/internal/socket.SockaddrToTCPOrUnixAddr g[07]: ->github.com/panjf2000/gnet/internal/socket.sockaddrInet6ToIPAndZone g[07]: ->github.com/panjf2000/gnet/internal/socket.ip6ZoneToString g[07]: <-github.com/panjf2000/gnet/internal/socket.ip6ZoneToString g[07]: <-github.com/panjf2000/gnet/internal/socket.sockaddrInet6ToIPAndZone g[07]: <-github.com/panjf2000/gnet/internal/socket.SockaddrToTCPOrUnixAddr g[07]: ->github.com/panjf2000/gnet.(*roundRobinLoadBalancer).next g[07]: <-github.com/panjf2000/gnet.(*roundRobinLoadBalancer).next g[07]: ->github.com/panjf2000/gnet.newTCPConn g[07]: ->github.com/panjf2000/gnet/pool/ringbuffer.Get g[07]: ->github.com/panjf2000/gnet/pool/ringbuffer.(*Pool).Get g[07]: ->github.com/panjf2000/gnet/ringbuffer.New g[07]: <-github.com/panjf2000/gnet/ringbuffer.New g[07]: <-github.com/panjf2000/gnet/pool/ringbuffer.(*Pool).Get g[07]: <-github.com/panjf2000/gnet/pool/ringbuffer.Get g[07]: ->github.com/panjf2000/gnet/pool/ringbuffer.Get g[07]: ->github.com/panjf2000/gnet/pool/ringbuffer.(*Pool).Get g[07]: ->github.com/panjf2000/gnet/ringbuffer.New g[07]: <-github.com/panjf2000/gnet/ringbuffer.New g[07]: <-github.com/panjf2000/gnet/pool/ringbuffer.(*Pool).Get g[07]: <-github.com/panjf2000/gnet/pool/ringbuffer.Get g[07]: <-github.com/panjf2000/gnet.newTCPConn g[07]: ->github.com/panjf2000/gnet/internal/netpoll.(*Poller).Trigger g[07]: ->github.com/panjf2000/gnet/internal/netpoll/queue.(*lockFreeQueue).Enqueue g[07]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[07]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[07]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[07]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[07]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[07]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[07]: ->github.com/panjf2000/gnet/internal/netpoll/queue.cas g[07]: <-github.com/panjf2000/gnet/internal/netpoll/queue.cas g[07]: ->github.com/panjf2000/gnet/internal/netpoll/queue.cas g[07]: <-github.com/panjf2000/gnet/internal/netpoll/queue.cas g[07]: <-github.com/panjf2000/gnet/internal/netpoll/queue.(*lockFreeQueue).Enqueue g[07]: <-github.com/panjf2000/gnet/internal/netpoll.(*Poller).Trigger g[07]: <-github.com/panjf2000/gnet.(*server).acceptNewConnection g[07]: ->github.com/panjf2000/gnet/internal/netpoll.(*eventList).shrink g[07]: <-github.com/panjf2000/gnet/internal/netpoll.(*eventList).shrink g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.(*lockFreeQueue).Dequeue g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.cas g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.cas g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.(*lockFreeQueue).Dequeue g[06]: ->github.com/panjf2000/gnet/internal/netpoll.(*Poller).AddRead g[06]: <-github.com/panjf2000/gnet/internal/netpoll.(*Poller).AddRead g[06]: ->github.com/panjf2000/gnet.(*eventloop).loopOpen g[06]: ->github.com/panjf2000/gnet.(*eventloop).addConn g[06]: <-github.com/panjf2000/gnet.(*eventloop).addConn g[06]: ->github.com/panjf2000/gnet.(*EventServer).OnOpened g[06]: <-github.com/panjf2000/gnet.(*EventServer).OnOpened g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: ->github.com/panjf2000/gnet.(*eventloop).handleAction g[06]: <-github.com/panjf2000/gnet.(*eventloop).handleAction g[06]: <-github.com/panjf2000/gnet.(*eventloop).loopOpen g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.(*lockFreeQueue).Dequeue g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.load g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.(*lockFreeQueue).Dequeue g[06]: ->github.com/panjf2000/gnet/internal/netpoll/queue.(*lockFreeQueue).Empty g[06]: <-github.com/panjf2000/gnet/internal/netpoll/queue.(*lockFreeQueue).Empty g[06]: ->github.com/panjf2000/gnet/internal/netpoll.(*eventList).shrink g[06]: <-github.com/panjf2000/gnet/internal/netpoll.(*eventList).shrink g[06]: ->github.com/panjf2000/gnet.(*eventloop).loopRead g[06]: ->github.com/panjf2000/gnet.(*conn).read g[06]: ->github.com/panjf2000/gnet.(*conn).Read g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: <-github.com/panjf2000/gnet.(*conn).Read g[06]: ->github.com/panjf2000/gnet.(*conn).ResetBuffer g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Reset g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Reset g[06]: <-github.com/panjf2000/gnet.(*conn).ResetBuffer g[06]: <-github.com/panjf2000/gnet.(*conn).read g[06]: ->github.com/panjf2000/gnet.(*EventServer).PreWrite g[06]: <-github.com/panjf2000/gnet.(*EventServer).PreWrite g[06]: ->github.com/panjf2000/gnet.(*conn).write g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: <-github.com/panjf2000/gnet.(*conn).write g[06]: ->github.com/panjf2000/gnet.(*conn).read g[06]: ->github.com/panjf2000/gnet.(*conn).Read g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: <-github.com/panjf2000/gnet.(*conn).Read g[06]: ->github.com/panjf2000/gnet.(*conn).ResetBuffer g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Reset g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Reset g[06]: <-github.com/panjf2000/gnet.(*conn).ResetBuffer g[06]: <-github.com/panjf2000/gnet.(*conn).read g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Write g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Write g[06]: <-github.com/panjf2000/gnet.(*eventloop).loopRead g[06]: ->github.com/panjf2000/gnet/internal/netpoll.(*eventList).shrink g[06]: <-github.com/panjf2000/gnet/internal/netpoll.(*eventList).shrink g[06]: ->github.com/panjf2000/gnet.(*eventloop).loopRead g[06]: ->github.com/panjf2000/gnet.(*eventloop).loopCloseConn g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).IsEmpty g[06]: ->github.com/panjf2000/gnet/internal/netpoll.(*Poller).Delete g[06]: <-github.com/panjf2000/gnet/internal/netpoll.(*Poller).Delete g[06]: ->github.com/panjf2000/gnet.(*eventloop).addConn g[06]: <-github.com/panjf2000/gnet.(*eventloop).addConn g[06]: ->github.com/panjf2000/gnet.(*EventServer).OnClosed g[06]: <-github.com/panjf2000/gnet.(*EventServer).OnClosed g[06]: ->github.com/panjf2000/gnet.(*conn).releaseTCP g[06]: ->github.com/panjf2000/gnet/pool/ringbuffer.Put g[06]: ->github.com/panjf2000/gnet/pool/ringbuffer.(*Pool).Put g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Len g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Len g[06]: ->github.com/panjf2000/gnet/pool/ringbuffer.index g[06]: <-github.com/panjf2000/gnet/pool/ringbuffer.index g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Reset g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Reset g[06]: <-github.com/panjf2000/gnet/pool/ringbuffer.(*Pool).Put g[06]: <-github.com/panjf2000/gnet/pool/ringbuffer.Put g[06]: ->github.com/panjf2000/gnet/pool/ringbuffer.Put g[06]: ->github.com/panjf2000/gnet/pool/ringbuffer.(*Pool).Put g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Len g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Len g[06]: ->github.com/panjf2000/gnet/pool/ringbuffer.index g[06]: <-github.com/panjf2000/gnet/pool/ringbuffer.index g[06]: ->github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Reset g[06]: <-github.com/panjf2000/gnet/ringbuffer.(*RingBuffer).Reset g[06]: <-github.com/panjf2000/gnet/pool/ringbuffer.(*Pool).Put g[06]: <-github.com/panjf2000/gnet/pool/ringbuffer.Put g[06]: <-github.com/panjf2000/gnet.(*conn).releaseTCP g[06]: <-github.com/panjf2000/gnet.(*eventloop).loopCloseConn g[06]: <-github.com/panjf2000/gnet.(*eventloop).loopRead g[06]: ->github.com/panjf2000/gnet/internal/netpoll.(*eventList).shrink g[06]: <-github.com/panjf2000/gnet/internal/netpoll.(*eventList).shrink
通过gnet-demo输出,我们可以清晰看到gnet接收一个连接,在这个连接上读写以及关闭这个连接的函数调用链,有了这个链条,我们再来阅读gnet源码就轻松许多了,即便有回调函数也没有问题。
上面输出的函数调用链的内容已经很多了。但如果你还不满足于这些,比如我还要跟踪到gnet依赖的golang.org/x/sys中,那可以利用相同思路,将golang.org/x/sys下载到本地,并通过functrace添加跟踪设施,并在gnet-demo中用replace换掉golang.org/x/sys,让其指向本地的sys包代码。如果觉得信息太多,可以通过gen命令做单个必要go源文件的跟踪信息添加,而不必要用批量方式。进一步的跟踪sys包的函数调用链的作业就留给大家了,这里就不深入了。
代码阅读完成后,我们只需在gnet目录下执行如下命令便可以恢复gnet原来的面貌:
$git checkout .
Go技术专栏“改善Go语⾔编程质量的50个有效实践”正在慕课网火热热销中!本专栏主要满足广大gopher关于Go语言进阶的需求,围绕如何写出地道且高质量Go代码给出50条有效实践建议,上线后收到一致好评!欢迎大家订
阅!
我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网热卖中,欢迎小伙伴们订阅学习!
讲师主页:tonybai_cn
讲师博客: Tony Bai
专栏:《改善Go语言编程质量的50个有效实践》
实战课:《Kubernetes实战:高可用集群搭建,配置,运维与应用》
免费课:《Kubernetes基础:开启云原生之门》