111

发布时间 2023-08-24 17:46:23作者: 小桔子1024

1. net/http初识

1.1 服务端

import (
    "fmt"
    "net/http"
)
 
func main() {
    http.HandleFunc("/smoke", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("ok!"))
    })
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        panic(err)
    }
}

说明:本地启动一个server端监听8080端口,并且提供路由/smole获取信息;

1.2 客户端

import (
    "fmt"
    "io/ioutil"
    "net/http"
)
 
func main() {
    resp, err := http.DefaultClient.Get("http://127.0.0.1:8080/smoke")
    if err != nil {
        fmt.Printf("get failed, err:%v\n", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("read from resp.body failed, err:%v\n", err)
        return
    }
    fmt.Println(string(body))
}

2. Server源码解析

2.1 Server

type Server struct {
    // server地址
    Addr string
    // 路由处理器
    Handler Handler
    ...
}

整个http服务端封装在Server中,Handler是Server中最核心的成员字段,实现从请求路径path到具体处理函数handler到注册和映射能力

在用户构造Server对象时,倘若其中的Handler字段未显示声明,则会取包下的单例对象DefaultServerMux(ServerMux类型)兜底

2.2 Handler

Handler是一个interface,定义了ServeHTTP方法

该方法的作用:根据http请求Request中的请求路径path映射到对应的handler处理函数,对请求进行处理响应

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

2.3 ServerMux

SeverMux是对Handler的具体实现,内部通过一个map维护从path到handler到映射关系

type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    es    []muxEntry // slice of entries sorted from longest to shortest.
    hosts bool       // whether any patterns contain hostnames
}

说明:读写锁mu保证ServerMux里map的安全性、es在前置匹配的时候用到

2.4 muxEntry

muxEntry是一个handle单元,内部包含了请求路径path(冗余了) + 处理函数handler两部分

type muxEntry struct {
    h       Handler
    pattern string
}

注意:2.1 Server中和Handler和2.4 muxEntry中的Handler协议是一样的,但是功能不一样(2.1 路由处理、2.4 注册的handler处理函数)

2.5 注册handler流程

http.HandleFunc --> ServeMux.HandleFunc --> ServeMux.Handle

2.5.1 http.HandleFunc

当用户直接使用http.HandleFunc注册handler时,则会将其注册到默认的DefaultServerMux中

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

2.5.2 ServeMux.HandleFunc

ServeMux.HandleFunc内部会将处理函数handler转为实现了ServeHTTP方法HandlerFunc类型,将其作为Handler interface的实现类注册到ServeMux的路由map中

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    mux.Handle(pattern, HandlerFunc(handler))
}

2.5.3 ServeMux.Handle 

将path和handler包装成一个muxEntry,以path为key注册到路由map(ServeMux.m)

响应模糊匹配机制,对于'/'结尾的path,根据path长度将muxEntry有序插入到数组ServeMux.es中

func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()
    // ...
    e := muxEntry{h: handler, pattern: pattern}
    mux.m[pattern] = e
    if pattern[len(pattern)-1] == '/' {
        mux.es = appendSorted(mux.es, e)
    }
    // ...
}

2.6 服务端启动

2.6.1 ListenAndServe

声明一个Server对象,嵌套执行ListenAndServe()方法

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

注意:如何传入的handler为空,会使用默认的DefaultServeMux

2.6.2 ListenAndServe()方法

根据用户传进来的端口,申请一个监听器listener,继而调用Server.Serve方法

func (srv *Server) ListenAndServe() error {
    // ...
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    return srv.Serve(ln)
}

2.6.3 Server.Serve

func (srv *Server) Serve(l net.Listener) error {
    baseCtx := context.Background()
    // ...
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    for {
        rw, err := l.Accept()
        // ...
        connCtx := ctx
     // ...
        c := srv.newConn(rw)
        // ...
        go c.serve(connCtx)
    }
}

如果有请求到来,就会走到Accept()方法,获取一个连接conn,启一个协程处理

2.6.4 serve方法

通过ctx将请求参数Request读出来,调用ServeHTTP做真正的请求处理

func (c *conn) serve(ctx context.Context) {
  // ...
  c.r = &connReader{conn: c}
  c.bufr = newBufioReader(c.r)
  c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

  for {
      w, err := c.readRequest(ctx)
    // ...
    serverHandler{c.server}.ServeHTTP(w, w.req)
    w.cancelCtx()	
  }
}

2.6.5 ServeHTTP方法

首先会判断Handler做判断,如果没声明,则取全局单例DefaultServeMux进行路由匹配,调用ServeHTTP处理

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
  handler := sh.srv.Handler
  if handler == nil {
    handler = DefaultServeMux
  }
  // ...
  handler.ServeHTTP(rw, req)
}

2.6.6 ServeMux.ServeHTTP

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
  // ...
  h, _ := mux.Handler(r)
  h.ServeHTTP(w, r)
}

2.6.7 ServeMux.Handler

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    // ...
    return mux.handler(host, r.URL.Path)
}

2.6.8 ServeMux.handler

func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
  mux.mu.RLock()
  defer mux.mu.RUnlock()
  // ...
  h, pattern = mux.match(path)
  // ...
  return
}

说明:当通过路由字典Server.m未命中handler,此时会启动模糊匹配,有两个核心规则

1)以'/'结尾的pattern才能被添加到Server.es数组中,才有资格参与模糊匹配

2)模糊匹配时,会找到一个与请求路径path前缀完全匹配且长度最长的pattern,其对应的handler为本次处理函数

3. Client源码解析

3.1 Client

Transport:负责http通信核心部分

Jar:cookie管理

Timeout:超时处理

type Client struct {
  // If nil, DefaultTransport is used.   Transport RoundTripper   // ...   Jar CookieJar   Timeout time.Duration }

3.2 RoundTripper

RoundTripper是通信模块的interface,需要实现方法RoundTrip,即通过传入请求Request,与服务端交互后获得响应Response

type RoundTripper interface {
    RoundTrip(*Request) (*Response, error)
}

3.3 Transport

Transport是RoundTripper的实现类,核心字段:

idleConn:空闲连接map,实现复用

DialContext:新连接生成器

type Transport struct {
  idleConn     map[connectMethodKey][]*persistConn // most recently used at end
  // ...
  DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
  // ...
}

3.4 Request

type Request struct {
  // 方法
   Method string
  // 请求路径   URL *url.URL   // 请求头   Header Header   // 请求参数内容   Body io.ReadCloser   // 服务器主机   Host string   // query请求参数   Form url.Values   // 响应参数   Response *Response   // 请求链路的上下文   ctx context.Context
  // ... }

  

3.5 请求流程  

 

  

 

 

ServeMux用于注册URL和handler的对应关系,并自动把请求转发到对应的handler进行处理

 

 

注意:http.ListenAndServe(":8080", nil)中的nil