<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用functrace輔助進(jìn)行Go項(xiàng)目源碼分析

          共 27715字,需瀏覽 56分鐘

           ·

          2021-08-29 07:26


          像跟蹤分布式服務(wù)調(diào)用那樣跟蹤Go函數(shù)調(diào)用鏈[1]:https://github.com/bigwhite/functrace。這個(gè)小工具不僅僅是分享給大家的,我自己在工作和學(xué)習(xí)時(shí)也在使用。最近發(fā)現(xiàn)這個(gè)小工具在閱讀和分析某個(gè)Go項(xiàng)目源碼時(shí)也能起到關(guān)鍵的輔助作用。這里就和大家簡單講解一下如何用functrace來輔助Go源碼閱讀和分析。

          程序員的日常離不開“源碼閱讀和分析”,日常閱讀代碼的姿勢無非是這么幾種(或幾種的組合):

          • 結(jié)合源碼編輯器或IDE提供的強(qiáng)大的源碼交叉索引和跳轉(zhuǎn)功能在一個(gè)龐大的源碼庫中建立起代碼間的聯(lián)系;
          • 將代碼跑起來,在代碼中加上一些print輸出,跟蹤執(zhí)行流并畫出;
          • 也有人喜歡用調(diào)試器從一點(diǎn)(通常是main)開始單步跟蹤執(zhí)行流。

          無論哪一種方式,最終只要時(shí)間夠長,態(tài)度到位,總是會(huì)將代碼分析出個(gè)七七八八的。

          就筆者來看,無論是哪種范式:命令式、面向?qū)ο蟆⒑瘮?shù)式,最終梳理出來的源碼脈絡(luò)都是建立在執(zhí)行基本單元(函數(shù)或方法)上,代碼的執(zhí)行主線(并發(fā)程序會(huì)有若干條)本質(zhì)上就是一條函數(shù)/方法調(diào)用鏈。只要把這條鏈理出來,代碼理解起來就不難了。上述的代碼閱讀方法實(shí)質(zhì)也是參照這個(gè)邏輯的。只是對于調(diào)用層次較深,還伴隨有回調(diào)的代碼,梳理調(diào)用鏈難度高、效率低。

          functrace最初用于跟蹤函數(shù)調(diào)用鏈(得益于Go核心開發(fā)團(tuán)隊(duì)公開的抽象語法樹AST API[2]),但如果在閱讀代碼時(shí)直接用functrace輸出函數(shù)調(diào)用鏈,那將大幅提高我們源碼閱讀分析的效率。下面我們就用一個(gè)樣例項(xiàng)目來試試如何用functrace梳理出代碼的執(zhí)行主線。

          我們以Go高性能、輕量級(jí)、非阻塞的事件驅(qū)動(dòng)網(wǎng)絡(luò)框架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

          接下來,我們下載要進(jìn)行源碼分析的gnet源碼:

          $git clone [email protected]:panjf2000/gnet.git

          我們進(jìn)入gnet目錄,現(xiàn)在我們可以使用gen命令為任意go源文件添加“跟蹤設(shè)施”了,比如:

          $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"
          ... ...

          我們可以這樣根據(jù)自己的需要在特定的go源文件上添加“跟蹤設(shè)施”,但是多數(shù)情況下,我們也可以通過腳本為項(xiàng)目內(nèi)所有g(shù)o源文件批量添加“跟蹤設(shè)施”,functrace項(xiàng)目提供了一個(gè)簡單的腳本batch_add_trace.sh[3],下面我們就來通過該腳本將gnet下的go源文件批量加上函數(shù)跟蹤設(shè)施:

          下載functrace源碼:

          $git clone https://github.com/bigwhite/functrace.git

          將functrace/scripts/batch_add_trace.sh 拷貝到上面gnet目錄下并執(zhí)行下面命令:

          # 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

          接下來我們編寫一個(gè)基于gnet的程序,我們就使用gnet參加TechEmpower[4]的那份代碼:

          //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)))
          }

          構(gòu)建這份代碼:

          $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務(wù)必不能省略,這個(gè)是開啟functrace的關(guān)鍵

          構(gòu)建后,我們來執(zhí)行構(gòu)建出的可執(zhí)行程序: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的執(zhí)行主線被清晰的打印出來,通過輸出的函數(shù)所在包我們可以輕松找到對應(yīng)的源文件。g[01]這goroutine顯然是main goroutine,整個(gè)程序的初始化線索通過跟蹤g[01]的函數(shù)鏈便一目了然。

          如果我們要看gnet是如何處理一個(gè)外部鏈接的,我們可以向gnet-demo建立一個(gè)連接,看看gnet-demo的輸出。

          我們通過curl命令向gnet-demo發(fā)起一個(gè)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接收一個(gè)連接,在這個(gè)連接上讀寫以及關(guān)閉這個(gè)連接的函數(shù)調(diào)用鏈,有了這個(gè)鏈條,我們再來閱讀gnet源碼就輕松許多了,即便有回調(diào)函數(shù)也沒有問題。

          上面輸出的函數(shù)調(diào)用鏈的內(nèi)容已經(jīng)很多了。但如果你還不滿足于這些,比如我還要跟蹤到gnet依賴的golang.org/x/sys中,那可以利用相同思路,將golang.org/x/sys下載到本地,并通過functrace添加跟蹤設(shè)施,并在gnet-demo中用replace換掉golang.org/x/sys,讓其指向本地的sys包代碼。如果覺得信息太多,可以通過gen命令做單個(gè)必要go源文件的跟蹤信息添加,而不必要用批量方式。進(jìn)一步的跟蹤sys包的函數(shù)調(diào)用鏈的作業(yè)就留給大家了,這里就不深入了。

          代碼閱讀完成后,我們只需在gnet目錄下執(zhí)行如下命令便可以恢復(fù)gnet原來的面貌:

          $git checkout .

          參考資料

          [1] 

          《像跟蹤分布式服務(wù)調(diào)用那樣跟蹤Go函數(shù)調(diào)用鏈》: https://mp.weixin.qq.com/s/zrM0I-CsEujAm6ho6AD79g

          [2] 

          抽象語法樹AST API: https://tip.golang.org/pkg/go/ast/

          [3] 

          batch_add_trace.sh: https://github.com/bigwhite/functrace/blob/main/scripts/batch_add_trace.sh

          [4] 

          TechEmpower: https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Go/gnet

          [5] 

          改善Go語?編程質(zhì)量的50個(gè)有效實(shí)踐: https://www.imooc.com/read/87

          [6] 

          Kubernetes實(shí)戰(zhàn):高可用集群搭建、配置、運(yùn)維與應(yīng)用: https://coding.imooc.com/class/284.html

          [7] 

          我愛發(fā)短信: https://51smspush.com/

          [8] 

          鏈接地址: https://m.do.co/c/bff6eed92687



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 48
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99 热在线观看 | 国产精品无码午夜福利 | 久久99精品久久久水蜜桃 | 国产午夜精品电影 | 日本黄色精品视频 |