go语言 通过http包搭建简单web服务器 对http包源码的略微分析
2017-11-03 20:48
926 查看
使用go的http包搭建简单的服务器
go run 运行,这个时候其实已经在8080端口监听http请求了。
浏览器中输入http://localhost:8080/就可以看到一行英文:
“Hello my friend!”了。
下面看一下http包的源码,了解 web 服务器处理 http 协议的过程.
主要分析main()中的两个函数:http.HandleFunc(“/”, sayhelloName)和http.ListenAndServe(“:8080”, nil)
先看http.HandleFunc(“/”, sayhelloName)的定义。该函数给DefaultServeMux中相应的路由注册处理函数。
继续追踪方法DefaultServeMux.HandleFunc(pattern, handler)
其中mux.Handle声明如下
我们发现方法DefaultServeMux.HandleFunc的参数handler的类型是func(ResponseWriter, *Request),然而方法mux.Handle的第二个参数应该是Handler接口类型。这意味着HandlerFunc(handler)使得HandleFunc方法的第二个参数handler实现了Handler接口,接下来就看看HandlerFunc()怎么实现这一点。
Handler接口:
HandlerFunc类型的定义
个人认为,mux.Handle(pattern, HandlerFunc(handler))中的
HandlerFunc(handler)是一个类型转换,把对应函数转换成HandlerFunc类型,而HandlerFunc类型实现了ServeHTTP接口,这样就满足了mux.Handle方法的第二个参数需要实现Handler接口的要求。
通过阅读源码,我们学习到了把传入的函数变成相应接口的方法。
接下来分析怎么实现相应路由处理函数的注册:
ServeMux结构如下:
mu是锁,由于请求涉及并发处理,这里需要锁机制。
m则是路由规则,一个string(注册处理函数的路由表达式)对应一个muxEntry
下面看一下muxEntry:
h就是我们之前提到的Handler接口,就是对应的处理函数。
func (mux *ServeMux) Handle(pattern string, handler Handler)的定义:
分析完了http.HandleFunc(“/”, sayhelloName),接下来分析http.ListenAndServe(“:8080”, nil)。
跟踪http.ListenAndServe(“:8080”, nil)定义一直到
func (srv *Server) Serve(l net.Listener) error的定义。监听的套接字使用accept函数接受客户端发送的连接请求后,处理代码如下:
Go使用了goroutines来处理Conn的读写事件,这样每个请求都能保持独立,相互不会阻塞。
客户端的每次请求都会创建一个Conn。
上面代码的第三行c.serve(ctx),该方法的定义中的serverHandler{c.server}.ServeHTTP(w, w.req)用于处理请求,参数w是*response类型,response是结构体,代表着服务端的一个HTTP response。w.req是请求。根据serverHandler结构体中的*Server来确定是哪个server处理该请求。
如果之前调用的http.ListenAndServe(“:8080”, nil)的第二个参数是nil,则ServeHTTP会使用DefaultServeMux,默认处理逻辑来处理请求。
DefaultServeMux实现的ServeHTTP如下:
调用mux.Handler(r),返回请求路由对应的处理Handler,然后执行h.ServeHTTP(w,r)
mux.Handler(r)代码如下:
上面代码第19行的mux.handler(host, r.URL.Path)代码如下:
mux.match代码如下:
根据用户请求的URL和mux储存的map去匹配,匹配到的话返回储存的handler,调用它的ServeHTTP接口就可以执行处理函数,处理请求了。
同时,我们也可以自己定义一个路由管理器,而不使用默认的DefaultServeMux。http.ListenAndServe的第二个参数是用来设置自定义路由管理器的,它是一个Handler接口,只要实现了Handler接口就可以完成路由管理器的处理规则的设置。我们可以在自己实现的路由管理器的,由Handler接口定义的ServeHTTP方法里面实现自定义路由功能。栗子如下:
浏览器输入http://localhost:8080/会显示Hello my friend!
输入http://localhost:8080/abc会显示the path of this page is /abc
package main import ( "fmt" "log" "net/http" "strings" ) func sayhelloName(w http.ResponseWriter, r *http.Request) { r.ParseForm() //解析参数,默认是不会解析的 fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息 fmt.Println("path", r.URL.Path) fmt.Println("scheme", r.URL.Scheme) fmt.Println(r.Form["friend"]) for k, v := range r.Form { fmt.Println("key:", k) fmt.Println("val:", strings.Join(v, "")) } fmt.Fprintf(w, "Hello my friend!") //这个写入到w的是输出到客户端的 } func main() { http.HandleFunc("/", sayhelloName) //设置访问的路由 err := http.ListenAndServe(":8080", nil) //设置监听的端口 if err != nil { log.Fatal("ListenAndServe: ", err) } }
go run 运行,这个时候其实已经在8080端口监听http请求了。
浏览器中输入http://localhost:8080/就可以看到一行英文:
“Hello my friend!”了。
下面看一下http包的源码,了解 web 服务器处理 http 协议的过程.
主要分析main()中的两个函数:http.HandleFunc(“/”, sayhelloName)和http.ListenAndServe(“:8080”, nil)
先看http.HandleFunc(“/”, sayhelloName)的定义。该函数给DefaultServeMux中相应的路由注册处理函数。
// 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.HandleFunc(pattern, handler)
// HandleFunc registers the handler function for the given pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) }
其中mux.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)
我们发现方法DefaultServeMux.HandleFunc的参数handler的类型是func(ResponseWriter, *Request),然而方法mux.Handle的第二个参数应该是Handler接口类型。这意味着HandlerFunc(handler)使得HandleFunc方法的第二个参数handler实现了Handler接口,接下来就看看HandlerFunc()怎么实现这一点。
Handler接口:
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
HandlerFunc类型的定义
// 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) }
个人认为,mux.Handle(pattern, HandlerFunc(handler))中的
HandlerFunc(handler)是一个类型转换,把对应函数转换成HandlerFunc类型,而HandlerFunc类型实现了ServeHTTP接口,这样就满足了mux.Handle方法的第二个参数需要实现Handler接口的要求。
通过阅读源码,我们学习到了把传入的函数变成相应接口的方法。
接下来分析怎么实现相应路由处理函数的注册:
ServeMux结构如下:
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry hosts bool // whether any patterns contain hostnames }
mu是锁,由于请求涉及并发处理,这里需要锁机制。
m则是路由规则,一个string(注册处理函数的路由表达式)对应一个muxEntry
下面看一下muxEntry:
type muxEntry struct { explicit bool h Handler pattern string }
h就是我们之前提到的Handler接口,就是对应的处理函数。
func (mux *ServeMux) Handle(pattern string, handler Handler)的定义:
// 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()
//锁机制
...//处理错误情况,如路由不合理,handler为nil,重复注册等
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}//这句代码实现了对应处理函数的注册
//后面代码涉及重定向就不贴出来了
}
分析完了http.HandleFunc(“/”, sayhelloName),接下来分析http.ListenAndServe(“:8080”, nil)。
跟踪http.ListenAndServe(“:8080”, nil)定义一直到
func (srv *Server) Serve(l net.Listener) error的定义。监听的套接字使用accept函数接受客户端发送的连接请求后,处理代码如下:
c := srv.newConn(rw) c.setState(c.rwc, StateNew) // before Serve can return go c.serve(ctx)
Go使用了goroutines来处理Conn的读写事件,这样每个请求都能保持独立,相互不会阻塞。
客户端的每次请求都会创建一个Conn。
上面代码的第三行c.serve(ctx),该方法的定义中的serverHandler{c.server}.ServeHTTP(w, w.req)用于处理请求,参数w是*response类型,response是结构体,代表着服务端的一个HTTP response。w.req是请求。根据serverHandler结构体中的*Server来确定是哪个server处理该请求。
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) }
如果之前调用的http.ListenAndServe(“:8080”, nil)的第二个参数是nil,则ServeHTTP会使用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(r),返回请求路由对应的处理Handler,然后执行h.ServeHTTP(w,r)
mux.Handler(r)代码如下:
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { // CONNECT requests are not canonicalized. if r.Method == "CONNECT" { return mux.handler(r.Host, r.URL.Path) } // All other requests have any port stripped and path cleaned // before passing to mux.handler. host := stripHostPort(r.Host) path := cleanPath(r.URL.Path) if path != r.URL.Path { _, pattern = mux.handler(host, path) url := *r.URL url.Path = path return RedirectHandler(url.String(), StatusMovedPermanently), pattern } //前面代码进行相应处理,得到host和URL.Path return mux.handler(host, r.URL.Path) }
上面代码第19行的mux.handler(host, r.URL.Path)代码如下:
// handler is the main implementation of Handler. // The path is known to be in canonical form, except for CONNECT methods. 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 }
mux.match代码如下:
// Find a handler on a handler map given a path string. // Most-specific (longest) pattern wins. 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 } ...//后面是没有匹配的处理函数时的处理方法 }
根据用户请求的URL和mux储存的map去匹配,匹配到的话返回储存的handler,调用它的ServeHTTP接口就可以执行处理函数,处理请求了。
同时,我们也可以自己定义一个路由管理器,而不使用默认的DefaultServeMux。http.ListenAndServe的第二个参数是用来设置自定义路由管理器的,它是一个Handler接口,只要实现了Handler接口就可以完成路由管理器的处理规则的设置。我们可以在自己实现的路由管理器的,由Handler接口定义的ServeHTTP方法里面实现自定义路由功能。栗子如下:
package main import ( "fmt" "log" "net/http" ) type MyServeMux struct { } func (mux *MyServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Println(r.URL.Path) if r.URL.Path == "/" { sayhelloName(w, r) return } if r.URL.Path == "/abc" { saypagePath(w, r) return } http.NotFound(w, r) return } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello my friend!") } func saypagePath(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "the path of this page is /abc") } func main() { mymux := &MyServeMux{} err := http.ListenAndServe(":8080", mymux) if err != nil { log.Fatal("ListenAndServe: ", err) } }
浏览器输入http://localhost:8080/会显示Hello my friend!
输入http://localhost:8080/abc会显示the path of this page is /abc
相关文章推荐
- Go搭建简单服务器及http包源码分析
- Go语言进行web开发(一) 搭建一个简单的web服务器
- Mongoose源码分析之--简单的服务器搭建(C语言)
- 搭建一个简单的Go Web服务器
- Go利用net/http包搭建Web服务器
- 【源码剖析】tinyhttpd —— C 语言实现最简单的 HTTP 服务器
- 【源码剖析】tinyhttpd —— C 语言实现最简单的 HTTP 服务器
- 《Go语言入门》第一个Go语言Web程序——简单的Web服务器
- Go语言实现的一个简单Web服务器
- 【转】【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- 【源码剖析】tinyhttpd —— C 语言实现最简单的 HTTP 服务器
- Go语言实现简单Web服务器的方法
- 【源码剖析】tinyhttpd —— C 语言实现最简单的 HTTP 服务器
- go 语言实现一个简单的 web 服务器
- 【源码剖析】tinyhttpd —— C 语言实现最简单的 HTTP 服务器
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- 《Go语言入门》第一个Go语言Web程序——简单的Web服务器
- Go语言实现简单的一个静态WEB服务器
- go搭建一个简单web服务器