GO 三方库源码阅读姿势

参考内容:极客专栏:手把手带你写一个web框架

阅读顺序

库函数 > 结构定义 > 结构函数

​ 简单来说,就是当你在阅读一个代码库的时候,不应该从上到下阅读整个代码文档,而应 该先阅读整个代码库提供的对外库函数(function),再读这个库提供的结构 (struct/class),最后再阅读每个结构函数(method)

image-20230222160216925

查看库函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
go doc net/http(三方库名称) | grep "^func" 

func CanonicalHeaderKey(s string) string
func DetectContentType(data []byte) string
func Error(w ResponseWriter, error string, code int)
func Get(url string) (resp *Response, err error)
func Handle(pattern string, handler Handler)
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
func Head(url string) (resp *Response, err error)
func ListenAndServe(addr string, handler Handler) error
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error
func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser
func NewRequest(method, url string, body io.Reader) (*Request, error)
func NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error)
func NotFound(w ResponseWriter, r *Request)
func ParseHTTPVersion(vers string) (major, minor int, ok bool)
func ParseTime(text string) (t time.Time, err error)
func Post(url, contentType string, body io.Reader) (resp *Response, err error)
func PostForm(url string, data url.Values) (resp *Response, err error)
func ProxyFromEnvironment(req *Request) (*url.URL, error)
func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error)
func ReadRequest(b *bufio.Reader) (*Request, error)
func ReadResponse(r *bufio.Reader, req *Request) (*Response, error)
func Redirect(w ResponseWriter, r *Request, url string, code int)
func Serve(l net.Listener, handler Handler) error
func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, ...)
func ServeFile(w ResponseWriter, r *Request, name string)
func ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error
func SetCookie(w ResponseWriter, cookie *Cookie)
func StatusText(code int) string

​ 在这个库提供的方法中,我们去掉一些 NewSet 开头的函数,因为你从命名上可以看出,这些函数是对某个对象或者属性的设置。

剩下的函数大致可以分成三类:

  • 为服务端提供创建 HTTP 服务的函数,名字中一般包含 Serve 字样,比如 Serve、 ServeFile、ListenAndServe 等。

  • 为客户端提供调用 HTTP 服务的类库,以 HTTP 的 method 同名,比如 Get、Post、 Head 等。

  • 提供中转代理的一些函数,比如 ProxyURL、ProxyFromEnvironment 等。

查看结构定义(模块)

​ 我们过一遍这个库提供的所有 struct,看看核心模块有哪些,同样使用 go doc:

1
go doc net/http | grep "^type"| grep struct

​ 可以看到整个库最核心的几个结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type Client struct{ ... }
type Cookie struct{ ... }
type MaxBytesError struct{ ... }
type ProtocolError struct{ ... }
type PushOptions struct{ ... }
type Request struct{ ... }
type Response struct{ ... }
type ServeMux struct{ ... }
type Server struct{ ... }
type Transport struct{ ... }

看结构的名字或者 go doc 查看结构说明文档,能逐渐了解它们的功能:

  • Client 负责构建 HTTP 客户端;

  • Server 负责构建 HTTP 服务端;

  • ServerMux 负责 HTTP 服务端路由;

  • Transport、Request、Response、Cookie 负责客户端和服务端传输对应的不同模块。

​ 现在通过库方法(function)和结构体(struct),我们对整个库的结构和功能有大致印象 了。整个库承担了两部分功能,一部分是构建 HTTP 客户端,一部分是构建 HTTP 服务 端。

​ 构建的 HTTP 服务端除了提供真实服务之外,也能提供代理中转服务,它们分别由 Client 和 Server 两个数据结构负责。除了这两个最重要的数据结构之外,HTTP 协议的每个部 分,比如请求、返回、传输设置等都有具体的数据结构负责。

结构函数(能力)

​ 阅读具体的代码逻辑用 go doc 命令明显就不够了,你需要两个东西:

​ 一个是可以灵活进行代码跳转的 IDE,VS Code 和 GoLand 都是非常好的工具。以我们现 在要查看的 http.ListenAndServe 这个函数为例,我们可以从上面的例子代码中,直接通 过 IDE 跳转到这个函数的源码中阅读,有一个能灵活跳转的 IDE 工具是非常必要的。

​ 具体方法是将要分析的代码从入口处一层层记录下来,每个函数,我们只记录其核心代码,然后对每个核心代码一层层解析。记得把思维导图的结构设置为右侧分布,这样更直观。