博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
golang-net/http源码分析之http server
阅读量:4032 次
发布时间:2019-05-24

本文共 7960 字,大约阅读时间需要 26 分钟。

本文csdn博客链接:

本文qq空间链接:

golang-net/http源码分析之http server

1 简介

先看下net/http库中的例子

创建一个http server,简单的几条语句就可以了。

http.Handle("/foo", fooHandler)		http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {		fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))	})		log.Fatal(http.ListenAndServe(":8080", nil))

其中的接口部分如下

type HandlerFunc func(ResponseWriter, *Request)

2 路由

要理解http server那么就应该从路由开始。那么开始看源码

type ServeMux struct {	 		mu    sync.RWMutex	 		m     map[string]muxEntry	 		hosts bool // whether any patterns contain hostnames	 	}	 		 	type muxEntry struct {	 		explicit bool	 		h        Handler	 		pattern  string	 	}	 		 	// NewServeMux allocates and returns a new ServeMux.	 	func NewServeMux() *ServeMux { return new(ServeMux) }	 		 	// DefaultServeMux is the default ServeMux used by Serve.	 	var DefaultServeMux = &defaultServeMux	 		 	var defaultServeMux ServeMux

ServerMux则是一个http路由struct。里面的map包含了一个路由hash表。

另外上面的代码中,也形成了一个DefaultServeMux,默认的路由对象。

type ServeMux	    func NewServeMux() *ServeMux	    func (mux *ServeMux) Handle(pattern string, handler Handler)	    func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))	    func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)	    func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)

下面一个一个来做函数分析

2.1 Handle

func (mux *ServeMux) Handle(pattern string, handler Handler)

这个函数为注册路由表函数,功能就是创建muxEntry,然后将其放入到路由map中

func (mux *ServeMux) Handle(pattern string, handler Handler) {			mux.mu.Lock()			defer mux.mu.Unlock()					if pattern == "" {				panic("http: invalid pattern " + pattern)			}			if handler == nil {				panic("http: nil handler")			}			if mux.m[pattern].explicit {				panic("http: multiple registrations for " + pattern)			}					if mux.m == nil {				mux.m = make(map[string]muxEntry)			}						//主要的地方就是在这里			mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}					if pattern[0] != '/' {				mux.hosts = true			}					// Helpful behavior:			// If pattern is /tree/, insert an implicit permanent redirect for /tree.			// It can be overridden by an explicit registration.			n := len(pattern)			if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {				// If pattern contains a host name, strip it and use remaining				// path for redirect.				path := pattern				if pattern[0] != '/' {					// In pattern, at least the last character is a '/', so					// strings.Index can't be -1.					path = pattern[strings.Index(pattern, "/"):]				}				url := &url.URL{Path: path}				mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}	 		}	 	}

2.2 HandleFunc

HandleFunc就是对Handle的封装

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {  		mux.Handle(pattern, HandlerFunc(handler))  	}

2.3 Handler

Handler则是根据request的访问路径,查找相关的路由表,得到处理函数

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {			if r.Method != "CONNECT" {				if p := cleanPath(r.URL.Path); p != r.URL.Path {					_, pattern = mux.handler(r.Host, p)					url := *r.URL					url.Path = p					return RedirectHandler(url.String(), StatusMovedPermanently), pattern				}			}					return mux.handler(r.Host, r.URL.Path)		}				func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {			mux.mu.RLock()			defer mux.mu.RUnlock()					// Host-specific pattern takes precedence over generic ones			if mux.hosts {				h, pattern = mux.match(host + path)			}			if h == nil {				h, pattern = mux.match(path)			}			if h == nil {				h, pattern = NotFoundHandler(), ""			}			return		}

2.4 ServeHTTP

ServerHttp则是路由的入口

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {	//下面是处理url路径		if r.RequestURI == "*" {			if r.ProtoAtLeast(1, 1) {				w.Header().Set("Connection", "close")			}			w.WriteHeader(StatusBadRequest)			return		}		//查找handler		h, _ := mux.Handler(r)		//具体的处理		h.ServeHTTP(w, r)	}

2.5 DefaultServeMux

默认路由,只是对DefaultServeMux的一层封装

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }		func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {		 		DefaultServeMux.HandleFunc(pattern, handler)		 	}

3 Server

入口,创建并监听。

其实是创建了一个Server对象

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

看看server都提供哪些操作

type Server	    func (srv *Server) ListenAndServe() error	    func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error	    func (srv *Server) Serve(l net.Listener) error	    func (srv *Server) SetKeepAlivesEnabled(v bool)

下面我们跟踪过程

3.1 ListenAndServe

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(tcpKeepAliveListener{ln.(*net.TCPListener)})		}

其中Listen返回的是一个Listener接口

func Listen(net, laddr string) (Listener, error)				type Listener interface {	        // Accept waits for and returns the next connection to the listener.	        Accept() (Conn, error)		        // Close closes the listener.	        // Any blocked Accept operations will be unblocked and return errors.	        Close() error		        // Addr returns the listener's network address.	        Addr() Addr	}

3.2 Serve

真正的入口处

步骤:Accept–>srv.newConn–>c.serve(ctx)

func (srv *Server) Serve(l net.Listener) error {			defer l.Close()			if fn := testHookServerServe; fn != nil {				fn(srv, l)			}			var tempDelay time.Duration // how long to sleep on accept failure					if err := srv.setupHTTP2_Serve(); err != nil {				return err			}					// TODO: allow changing base context? can't imagine concrete			// use cases yet.			baseCtx := context.Background()			ctx := context.WithValue(baseCtx, ServerContextKey, srv)			ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())			for {			//调用Accept监听				rw, e := l.Accept()				if e != nil {					if ne, ok := e.(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", e, tempDelay)						time.Sleep(tempDelay)						continue					}					return e				}				tempDelay = 0				//获取新的链接				c := srv.newConn(rw)				c.setState(c.rwc, StateNew) // before Serve can return				//链接的处理处				go c.serve(ctx)			}		}

4 conn

4.1 newConn

创建了一个conn

func (srv *Server) newConn(rwc net.Conn) *conn {			c := &conn{				server: srv,				rwc:    rwc,			}			if debugServerConnections {				c.rwc = newLoggingConn("server", c.rwc)			}			return c		}

4.2 serve

conn里读取数据,然后进行处理

// Serve a new connection.	func (c *conn) serve(ctx context.Context) {		c.remoteAddr = c.rwc.RemoteAddr().String()		...		c.r = &connReader{r: c.rwc}		c.bufr = newBufioReader(c.r)		c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)			ctx, cancelCtx := context.WithCancel(ctx)		defer cancelCtx()			for {			w, err := c.readRequest(ctx)						...			//真正的处理地方			serverHandler{c.server}.ServeHTTP(w, w.req)			w.cancelCtx()			if c.hijacked() {				return			}			w.finishRequest()			if !w.shouldReuseConnection() {				if w.requestBodyLimitHit || w.closedRequestBodyEarly() {					c.closeWriteAndWait()				}				return			}			c.setState(c.rwc, StateIdle)		}	}

4.3 ServeHTTP

最终的地方。通过寻找路由,找到路由处理函数,然后对请求信息做响应处理。

type serverHandler struct {		srv *Server	}		func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {		handler := sh.srv.Handler		//nil则利用默认的路由		if handler == nil {			handler = DefaultServeMux		}		if req.RequestURI == "*" && req.Method == "OPTIONS" {			handler = globalOptionsHandler{}		}		handler.ServeHTTP(rw, req)	}

5 总结

  • 路由部分,构建map来存储路由处理函数
  • 链接处理部分,构建一个tcp listener,然后accept,构建conn
  • 然后通过conn,查找路由,找到处理函数,进行处理

龚浩华

qq 29185807 月牙寂-道长
2016年12月12日

qq 29185807

如果你觉得本文对你有帮助,可以转到你的朋友圈,让更多人一起学习。

第一时间获取文章,可以关注本人公众号:月牙寂道长,也可以扫码关注

在这里插入图片描述

转载地址:http://ypebi.baihongyu.com/

你可能感兴趣的文章
JVM面试要点:G1 垃圾收集器和如何做到可预测的停顿
查看>>
阿里巴巴倡导的数据中台,到底是什么东东
查看>>
揭底JVM,怎么能不了解G1垃圾收集器
查看>>
如何优雅的编程,lombok你怎么这么好用
查看>>
@RequestParam、@QueryParam等Spring常见参数注解区别,你知道吗
查看>>
玩转远程Debug,两步轻松开启IDEA远程调试
查看>>
Jmeter压测错误,Address already in use: connect
查看>>
Intellij IDEA常用快捷键,最全总结
查看>>
前端干货,超实用的JQuery小技巧
查看>>
Spring Boot 几个常见的核心注解
查看>>
程序员需要懂的一些Linux基本命令
查看>>
程序员需要掌握的一些网络协议汇总
查看>>
搞定Windows下的Hadoop环境安装
查看>>
设计模式之单例模式的五种写法
查看>>
Nginx开启Gzip压缩,使你的网页急速加载
查看>>
一文看清HBase的使用场景
查看>>
除了负载均衡,Nginx还可以做很多,限流、缓存、黑白名单
查看>>
解析zookeeper的工作流程
查看>>
搞定Java面试中的数据结构问题
查看>>
深入理解Apache Flink核心技术
查看>>