前言:go语言搭建文本服务很简单,几行代码就可以搭建一个稳定 高并发的web server
为什么相较于其他的语言GO的优势在哪里呢?我们带着问题往下看
一个go web服务器正常运行起来大概需要以下几个步骤:
- 创建listen socket,循环监听listen socke
- accept接受新的链接请求,并创建网络连接conn,然后开启一个goroutine负责处理该链接。
- 从该链接读取请求参数构造出http.Request对象,然后根据请求路径在路由表中查找,找到对应的上层应用的处理函数,把请求交给应用处理函数。
- 应用处理函数根据请求的参数等信息做处理,返回不同的信息给用户
- 应用层处理完该链接请求后关闭该链接(正常流程,如果是http alive则不关闭该链接)
这里面路由表是比较重要的,我们具体分析下http.Server是如何做路由的。
路由表实际上是一个map
key是路径 ==> “/hello”
value是该路径所对应的处理函数 ==> HelloServer
// hello world, the web server func HelloServer(w http.ResponseWriter, req *http.Request) { io.WriteString(w, "hello, world! ") } func main() { http.HandleFunc("/hello/", HelloServer) err := http.ListenAndServe(":8080", nil) if err != nil { log.Fatal("ListenAndServe: ", err) } }
路由表结构
go语言默认的路由表是 ServeMux,结构如下
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry //存放具体的路由信息 } type muxEntry struct { explicit bool h Handler pattern string } //muxEntry.Handler是一个接口 type Handler interface { ServeHTTP(ResponseWriter, *Request) } //这边可能会有疑惑 //http.HandleFunc("/hello/", HelloServer) //helloServer是一个function啊,并没有实现ServeHTTP接口啊 //这是因为虽然我们传入的是一个function,但是HandleFunc会把function转为实现了ServeHTTP接口的一个新类型 HandlerFunc。 /* func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) } type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } */
路由注册过程
注册过程其实就是往map中插入数据,值得注意的一个地方是如果
注册路径是/tree/并且没有/tree的路由信息,那么会在路由表中自动增加一条/tree的路由,
/tree的处理函数是重定向到/tree/。但是如果注册的是/tree是不会自动添加/tree/的路由的
// 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() mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} n := len(pattern) if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit { path := pattern fmt.Printf("redirect for :%s to :%s", pattern, path) mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern} } }
路由查找过程
路由查找过程就是遍历遍历路由表,找到最长匹配请求路径的路由信息并返回,如果找不到返回 NotFoundHandler
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { mux.mu.RLock() defer mux.mu.RUnlock() if h == nil { h, pattern = mux.match(path) } if h == nil { h, pattern = NotFoundHandler(), "" } return } func (mux *ServeMux) match(path string) (h Handler, pattern string) { var n = 0 for k, v := range mux.m { if !pathMatch(k, path) { continue } //找出匹配度最长的 if h == nil || len(k) > n { n = len(k) h = v.h pattern = v.pattern } } return } // 如果路由表中的路径是不以下划线结尾的 /hello //那么只有请求路径为/hello 完全匹配时才符合 //如果路由表中的注册路径是以下划线结尾的 /hello/ //那么请求路径只要满足/hello/* 就符合该路由 func pathMatch(pattern, path string) bool { n := len(pattern) if pattern[n-1] != '/' { return pattern == path } return len(path) >= n && path[0:n] == pattern }