介绍 golang net/http 源码

介绍 golang net/http 源码

1、源代码阅读

  • 阅读源代码是学习 golang 绕不开的任务,下文笔者将以 net/http 库 web 工作原理阅读为例,原理图如下:

在这里插入图片描述

  • 它的处理流程总体分为:注册路由;开始监听;处理请求;返回响应,流程图如下
    在这里插入图片描述

2、阅读源码的注意事项

  • 我们从入口函数 ListenAndServe 开始阅读分析代码:
  • 关注函数、方法参数中的 接口 和 函数 参数,是接口一定要了解接口的定义。
  • 随时查阅 API 文档,了解相关类型的属性与方法
  • 忽视任何错误处理、分支处理。尽管其中有许多有趣的东西,也要放弃
  • 其中特别注意闭包、匿名函数、匿名类型这些编程技巧
  • 特别注意接口断言语法 var.(type)
  • 线程要注意上下文对象(context)的构建

3、源代码分析

① 注册路由

1)DefaultServeMux & ServeMux
  • Mux,即 multiplexer 多路选择器。在多路数据传送过程,能根据需要将其中一路选出
  • 查看上文默认路由器的定义 DefaultServeMux,DefaultServeMux 是 ServeMux 的一个实例。
  • 在 ServeMux 中,m 是一个 map,存储路由和 handler 的关系,es 是一个切片 slice,将路由按长度从长到短排序存储。
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

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
}

2)HandleFunc & Handle
  • HandlerFunc 是一个函数类型,它实现了 Handler 接口的 ServeHTTP 方法

  • 查看 HandleFunc 源码,它调用 DefaultServeMux.HandleFunc() 注册路由

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}
  • 将传入的 handle function 和相应的 pattern 匹配
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler))
}
  • 查看上文 Handle() 的源码,它对传入的路径进行解析,路由匹配,然后设置 ServeMux.m 和 ServeMux.es,存放路由的匹配规则
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()

	if pattern == "" {
		panic("http: invalid pattern")
	}
	if handler == nil {
		panic("http: nil handler")
	}
	if _, exist := mux.m[pattern]; exist {
		panic("http: multiple registrations for " + pattern)
	}

	if mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
	e := muxEntry{h: handler, pattern: pattern}
	mux.m[pattern] = e
	if pattern[len(pattern)-1] == '/' {
		mux.es = appendSorted(mux.es, e)
	}

	if pattern[0] != '/' {
		mux.hosts = true
	}
}
  • 至此,pattern 和 handler 的路由注册完成

② 开启监听

1)ListenAndServe
  • 因为我们是通过 http.ListenAndServe() 绑定地址端口的,所以从它开始分析
  • ListenAndServe() 先实例化 Serve,然后调用 Server.ListenAndServe()
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}
  • 在 Server.ListenAndServe(),使用 TCP 协议创建了一个服务器,监听端口
func (srv *Server) ListenAndServe() error {
	if srv.shuttingDown() {
		return ErrServerClosed
	}
	// 取地址
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	// net.Listen 创建 socket 和绑定端口
	ln, err := net.Listen("tcp", addr)
	// 错误处理
	if err != nil {
		return err
	}
	// Server 再 Serve 链路上的请求
	return srv.Serve(ln)
}

③ 处理请求

1)Serve
  • 查看上文 srv.Serve(ln) 的源码
  • 它的功能为在 for 循环里持续监听 request,通过 accept 接受 request,并为每个 request 创建一个 goroutine 来处理。各个 request 之间是相互不影响,并发处理的
func (srv *Server) Serve(l net.Listener) error {
    ……
    // 循环监听
	for { 
		rw, err := l.Accept()
		// 错误处理
		if err != nil {
			select {
			case <-srv.getDoneChan():
				return ErrServerClosed
			default:
			}
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return err
		}
		// 复制服务线程
		connCtx := ctx
	    // 为一个新的连接修改上下文
		if cc := srv.ConnContext; cc != nil {
			connCtx = cc(connCtx, rw)
			if connCtx == nil {
				panic("ConnContext returned nil")
			}
		}
		tempDelay = 0
		// 创建连接,并初始化
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew)
		// go 协程处理连接
		go c.serve(connCtx)
	}
}

2)serve
  • 查看上文 c.serve (connCtx) 的源码
  • 它用于处理一个连接,用 readRequest 读取数据,解析 request 中的 Header 和 Body。接着通过 serverHandler 处理 request 和 response
func (c *conn) serve(ctx context.Context) {
	c.remoteAddr = c.rwc.RemoteAddr().String()
	ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
	// 连接断开后处理
	// ……
	if tlsConn, ok := c.rwc.(*tls.Conn); ok {
		// 设置 server 维持时间 和 tls handshake 失败处理
		// ···
		c.tlsState = new(tls.ConnectionState)
		*c.tlsState = tlsConn.ConnectionState()
		if proto := c.tlsState.NegotiatedProtocol; validNextProto(proto) {
			if fn := c.server.TLSNextProto[proto]; fn != nil {
				h := initALPNRequest{ctx, tlsConn, serverHandler{c.server}}
				fn(c.server, tlsConn, h)
			}
			return
		}
	}
	ctx, cancelCtx := context.WithCancel(ctx)
	c.cancelCtx = cancelCtx
	defer cancelCtx()

	c.r = &connReader{conn: c}
	c.bufr = newBufioReader(c.r)
	c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

	for {
		w, err := c.readRequest(ctx)
		// http request 错误处理
        // ……
		req := w.req
		if req.expectsContinue() {
			if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
				// Wrap the Body reader with one that replies on the connection
				req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
				w.canWriteContinue.setTrue()
			}
		} else if req.Header.get("Expect") != "" {
			w.sendExpectationFailed()
			return
		}

		c.curReq.Store(w)
		if requestBodyRemains(req.Body) {
			registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
		} else {
			w.conn.r.startBackgroundRead()
		}
		serverHandler{c.server}.ServeHTTP(w, w.req)
		w.cancelCtx()
        // ……
		w.finishRequest()
		// ……
		c.setState(c.rwc, StateIdle)
		c.curReq.Store((*response)(nil))
        // 判断连接存活
        if !w.conn.server.doKeepAlives() {
			return
		}
		if d := c.server.idleTimeout(); d != 0 {
			c.rwc.SetReadDeadline(time.Now().Add(d))
			if _, err := c.bufr.Peek(4); err != nil {
				return
			}
		}
		c.rwc.SetReadDeadline(time.Time{})
	}
}

④ 返回响应

1)serverHandler & ServeHTTP
  • 查看上文 serverHandler 的源码,它用于实现服务和处理的隔离
  • 在 ServerHTTP 中,设置 handler,如果 handler 为 nil,默认使用 DefaultServeMux
  • Handler 是一个服务处理回调接口
  • 实现 ServeHTTP(ResponseWriter, *Request) 方法的对象都可以与客户端会话
type serverHandler struct {
	srv *Server
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
	handler := sh.srv.Handler
	if handler == nil {
		handler = DefaultServeMux
	}
	if req.RequestURI == "*" && req.Method == "OPTIONS" {
		handler = globalOptionsHandler{}
	}
	handler.ServeHTTP(rw, req)
}

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

type HandlerFunc func(ResponseWriter, *Request)

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

2)match
  • match 函数实现了,根据路由找到对应的 handler。m 表存储了 pattern 和 handler 处理器函数的 map[string]muxEntry。优先查找 m 表,如果找不到,则在 es 表中进行匹配,路径长的优先匹配。
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
	// Check for exact match first.
	v, ok := mux.m[path]
	if ok {
		return v.h, v.pattern
	}

	// Check for longest valid match.  mux.es contains all patterns
	// that end in / sorted from longest to shortest.
	for _, e := range mux.es {
		if strings.HasPrefix(path, e.pattern) {
			return e.h, e.pattern
		}
	}
	return nil, ""
}

4、总结

  • 服务器的工作流程其实就是:创建 ServerSocket,绑定并 listen,accept 连接,创建 go 线程服务连接
  • Go 通过 ServeMux 实现了路由 multiplexer 来管理路由,并且设计一个 Handler 接口提供 ServeHTTP 实现 handler ,用于处理 request 并 response。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值