看了一下net/http包

浅析net/http

好tm难,理不清。

先来一段代码

package main

import (
	"net/http"
)

func Index(w http.ResponseWriter, r *http.Request){
	w.Write([]byte("hello world"))
}

func main() {
	http.HandleFunc("/",Index)
	http.ListenAndServe(":8080",nil)
}

这段代码做了什么呢?

简单来说,就是我们访问本机的8080端口加上路径就会调用Index给我们在网页上返回“hello world”,那这是怎么一个过程呢?

我们从HandleFunc与ListenAndServe两个函数入手

HandleFunc

其代码和注释如下:

这个函数的任务就是把handler方法注册,和pattern指定的参数绑定上。

handler必须是func(ResponseWriter, *Request) 类型的方法,ResponseWriter实现了io.Writer接口,可以直接使用.write()方法或使用fmt.Fprintln()向其中写入内容

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

这里有个DefaultServeMux,这个对象是net/http包的的全局变量,它的类型是

ServeMux

image-20220408173419140

结构如下

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
}

DefaultServeMux是这个包给我们提供的默认实例,我们也可以自己声明一个ServeMux对象进行使用。

函数注册流程

这里展示了两个HandleFunc,第一个以ServeMux的指针类型为接收器,第二个没有接收器,会在内部通过DefaultServeMux调用第一个,注册进默认实例。

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


// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

然后真正的注册就来了,那就是Handle函数

image-20220408175538724

这个函数的第二个参数HandlerFunc(handler)是一个类型转换,转换为HandlerFunc类型,而HandlerFunc通过实现ServeHTTP方法,而实现了Handler接口,Go语言中允许为函数类型定义方法

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

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

Handle函数的定义就是如下这样

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

// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

它只能接收Handler类型。

随后Handle方法将转换后的方法注册到ServeMux

ListenAndServe

函数注册后,调用ListenAndServe(":8080",nil)监听本机的8080端口。

// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

这个函数创建了一个Server对象,Server结构体有比较多的字段,我们可以用这些字段来调节Web服务器参数

// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
	Addr string
	Handler Handler 
	TLSConfig *tls.Config
	ReadTimeout time.Duration
	ReadHeaderTimeout time.Duration
	WriteTimeout time.Duration
	IdleTimeout time.Duration
	MaxHeaderBytes int
	TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
	ConnState func(net.Conn, ConnState)
	ErrorLog *log.Logger
	BaseContext func(net.Listener) context.Context
	ConnContext func(ctx context.Context, c net.Conn) context.Context
	inShutdown atomicBool // true when server is in shutdown
	disableKeepAlives int32     // accessed atomically.
	nextProtoOnce     sync.Once // guards setupHTTP2_* init
	nextProtoErr      error     // result of http2.ConfigureServer if used
	mu         sync.Mutex
	listeners  map[*net.Listener]struct{}
	activeConn map[*conn]struct{}
	doneChan   chan struct{}
	onShutdown []func()
}

随后由Server调用“ListenAndServe”

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

这个方法做的事情就是创建一个Listener ln,然后将ln作为参数交给Server.Serve方法执行。

Serve方法做的事情就是开启一个无限for循环,然后不断调用Listener.Accept()方法,去接收新连接,然开启新的goroutine处理新连接。

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, runHooks) // before Serve can return
		go c.serve(connCtx)
	}
}

这个方法中用到了指数退避策略。就是Accept发生错误后,将error转换为net.Error类型,然后判断这个错误是否是临时的,如果是临时的,就Sleep一段时间后再试。每发生一次sleep时间翻倍,最多1s。获得新连接后封装出一Conn对象,创建一个goroutine运行其serve()方法。

关于对Accept错误的处理:这种处理方式好像在gRPC中也有

临时错误可能有很多原因,比如等待过程中有中断要处理,那么linux系统可能返回EAGAIN错误(资源临时不可用),这种请求应该重试他。

而serve方法我们忽他中间 的很多步骤,它的运行大概是这样的流程:

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

就是不断读取客户端发起的请求,创建serverHandler对象调用其ServeHTTP()方法去处理请求,然后做清理工作。

ServerHandler相关的代码如下

// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
	srv *Server
}

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

	handler.ServeHTTP(rw, req)
}

handler := sh.srv.Handler就是从Server对象中获取Handler,就是http.ListrnAndServe()的第二个参数,如果设为nil,这个东西就是DefaultServeMux,会调用DefaultServeMux的ServeHTTP()方法。

就是这个方法

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
	if r.RequestURI == "*" {
		if r.ProtoAtLeast(1, 1) {
			w.Header().Set("Connection", "close")
		}
		w.WriteHeader(StatusBadRequest)
		return
	}
	h, _ := mux.Handler(r)
	h.ServeHTTP(w, r)
}

mux.Handler会根据请求方法、请求主机、请求url的路径返回格式的handler。

在匹配中要注意:

  • 若注册了/目录,所以未匹配上的就匹配到/
  • 最长前缀匹配,注册了/abc/,则/abc/d回匹配到/abc
  • 注册路径只有以/结尾才能出发如若注册了/abc,则/abc/a匹配不到/abc。

第三的Go Web框架大多是基于net/http实现自己的ServeMux对象。

大致过程图

posted @ 2022-04-17 17:00  博客是个啥?  阅读(47)  评论(0编辑  收藏  举报