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

本文csdn博客链接:http://blog.youkuaiyun.com/screscent/article/details/53583764
本文qq空间链接:http://user.qzone.qq.com/29185807/blog/1481529299

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

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

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

在这里插入图片描述

Administrator@DESKTOP-N9EAGIT MINGW64 /e/2025Study/note/Code $ go clean -i github.com/go-delve/delve/cmd/dlv no required module provides package github.com/go-delve/delve/cmd/dlv; to add it: go get github.com/go-delve/delve/cmd/dlv Administrator@DESKTOP-N9EAGIT MINGW64 /e/2025Study/note/Code $ go get github.com/go-delve/delve/cmd/dlv go: added github.com/cilium/ebpf v0.11.0 go: added github.com/cosiner/argv v0.1.0 go: added github.com/cpuguy83/go-md2man/v2 v2.0.6 go: added github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d go: added github.com/go-delve/delve v1.25.2 go: added github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62 go: added github.com/google/go-dap v0.12.0 go: added github.com/hashicorp/golang-lru v1.0.2 go: added github.com/inconshreveable/mousetrap v1.1.0 go: added github.com/mattn/go-colorable v0.1.13 go: added github.com/mattn/go-isatty v0.0.20 go: added github.com/mattn/go-runewidth v0.0.13 go: added github.com/rivo/uniseg v0.2.0 go: added github.com/russross/blackfriday/v2 v2.1.0 go: added github.com/spf13/cobra v1.9.1 go: added github.com/spf13/pflag v1.0.6 go: added go.starlark.net v0.0.0-20231101134539-556fd59b42f6 go: added golang.org/x/arch v0.11.0 go: added golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 go: added golang.org/x/sync v0.8.0 go: added golang.org/x/sys v0.26.0 go: added golang.org/x/telemetry v0.0.0-20241106142447-58a1122356f5 Administrator@DESKTOP-N9EAGIT MINGW64 /e/2025Study/note/Code $ go install github.com/go-delve/delve/cmd/dlv@latest Administrator@DESKTOP-N9EAGIT MINGW64 /e/2025Study/note/Code $ dlv version Delve Debugger Version: 1.25.2 Build: $Id: af3df277866d7175e816eab14e56611053d4cdea $ Administrator@DESKTOP-N9EAGIT MINGW64 /e/2025Study/note/Code $ 还是失败
最新发布
10-03
<think>我们面对的问题是:在Go 1.24.7环境下,更新Delve到1.25.2版本后仍然出现版本不兼容的警告。 根据引用[4]提到的情况,即使更新了Delve,有时也可能因为环境配置问题导致兼容性问题。 解决思路: 1. 确认当前Go版本:确保确实是Go 1.24.7 2. 确认Delve安装版本:确保安装的Delve是1.25.2(目前最新版本,支持Go 1.24) 3. 检查Delve的安装路径和GOPATH/bin是否在系统PATH中,确保命令行使用的是新安装的Delve 4. 如果使用IDE(如VSCode或Goland),检查IDE配置中使用的Delve路径是否正确指向新版本 5. 清除旧版本残留:有时候旧版本没有完全卸载,导致新版本未被使用 具体步骤: 步骤1:确认Go版本 在终端运行: ```bash go version ``` 输出应为:`go version go1.24.7 ...` 步骤2:确认Delve版本 安装最新版本Delve(如果你已经安装过,可以先卸载再安装): ```bash # 卸载旧版本(如果有) go clean -i github.com/go-delve/delve/cmd/dlv # 安装最新版本 go install github.com/go-delve/delve/cmd/dlv@latest ``` 然后检查版本: ```bash dlv version ``` 输出应包含类似:`Version: 1.25.2` 并且包含 `Supports up to goversion: Go 1.24` 注意:Delve 1.25.2是支持Go 1.24的(根据其Release Notes)。 步骤3:检查PATH 确保安装的Delve(通常在`$GOPATH/bin`或`$HOME/go/bin`)在系统的PATH环境变量中,并且优先级高于旧版本可能存在的其他路径(如`/usr/local/bin`等)。 步骤4:IDE配置 如果你在IDE中调试(如VSCode或Goland),需要确保IDE使用的是新安装的Delve。 对于VSCode: - 确保安装了Go扩展。 - 在设置(Settings)中搜索`go.delveConfig`,检查`dlvPath`是否指向新安装的Delve可执行文件。可以设置为绝对路径(例如:`/your/gopath/bin/dlv`)或者如果已经在PATH中,可以留空。 对于Goland: - 打开设置(Settings)> Go > Debug,检查“Delve”配置中的路径是否为新的Delve路径。 步骤5:清除旧版本残留 如果系统中其他地方(如`/usr/local/bin`)有旧版本的Delve,建议删除或重命名,以确保系统找到的是新版本。 如果上述步骤仍然不能解决问题,可以尝试以下额外步骤: 步骤6:使用源码编译安装Delve 有时通过`go install`安装可能因为网络问题或缓存问题未成功安装最新版本,可以尝试从源码编译: ```bash git clone https://github.com/go-delve/delve.git cd delve git checkout v1.25.2 # 确保切换到最新支持的版本 go install github.com/go-delve/delve/cmd/dlv ``` 步骤7:检查Go环境配置 确保你的Go环境变量设置正确,特别是`GOPATH`和`GOROOT`。可以通过`go env`命令查看。 步骤8:重启IDE和终端 有时环境变量更新后,需要重启IDE和终端才能生效。 引用[3]提到Go 1.24.4修复了一些问题,所以使用Go 1.24.7应该是没有问题的。另外,Delve的版本支持信息可以在其GitHub仓库的Release Notes中查看。 如果问题依旧,请参考Delve官方文档或提交Issue。 相关问题: 1. 如何确认Delve是否正确安装? 2. Goland中如何指定Delve的路径? 3. 更新Delve后,调试时仍然报版本不兼容,如何彻底卸载旧版本? </think>要解决Go 1.24.7环境下Delve版本过旧(即使更新到1.25.2后仍失败)的问题,请按照以下步骤操作: ### 📌 根本原因分析 1. **版本映射关系错误** Delve 1.25.2 官方支持Go 1.24.x,但需要确认具体版本映射关系(Go 1.24.7 是Go 1.24系列的补丁版本) 2. **环境污染问题** 旧版本残留文件可能导致新版本无法正确加载 3. **工具链兼容性** Go 1.24.7 包含安全修复(如channel哈希行为变更[^3]),需完全兼容的调试器 ### 🔧 解决方案步骤 #### 1. 完全卸载旧版本 ```bash # 清理所有安装路径 rm -rf $(go env GOPATH)/bin/dlv* # 删除二进制 go clean -modcache github.com/go-delve/delve # 清除源码缓存 sudo rm /usr/local/bin/dlv # 清除系统级安装(如存在) ``` #### 2. 安装兼容版本 ```bash # 安装官方认证的兼容版本 go install github.com/go-delve/delve/cmd/dlv@v1.25.2 ``` #### 3. 验证安装配置 ```bash # 检查版本映射关系 dlv debug --check-go-version # 预期输出: Compatible with Go 1.24.x ``` #### 4. IDE配置修正(VSCode示例) ```json // settings.json { "go.delveConfig": { "dlvPath": "$(go env GOPATH)/bin/dlv", "apiVersion": 2, "checkGoVersion": false // 临时禁用版本检查 } } ``` #### 5. 环境变量隔离 ```bash # 创建纯净环境 export GO111MODULE=on export GOPATH=$(mktemp -d) # 新建临时GOPATH go clean -cache # 清除构建缓存 ``` ### ⚠️ 注意事项 1. 使用Go 1.24.7时需验证所有依赖库的兼容性[^3] 2. 如仍失败,考虑降级Go版本至Delve明确支持的版本(如Go 1.22.x[^2]) 3. 对于旧项目,可锁定Delve版本为v1.4.0(适配Go 1.15.x[^1]) ### 📚 验证方法 ```bash # 启动调试监听 dlv debug --headless --listen=:2345 --api-version=2 ``` 预期输出: ```text API server listening at: 127.0.0.1:2345 Debugger for Go version 1.25.2 (API level 2) ``` > **关键提示**:Go 1.24+ 对底层运行时做了重大调整,若Delve日志显示`unsupported runtime version`,需等待官方发布适配补丁[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值