http.ServeMux解析

shanks · · 16638 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

web server概述

使用go语言搭建一个web服务器是很简单的,几行代码就可以搭建一个稳定的高并发的web server。

  1. // hello world, the web server
  2. func HelloServer(w http.ResponseWriter, req *http.Request) {
  3. io.WriteString(w, "hello, world!\n")
  4. }
  5. func main() {
  6. http.HandleFunc("/hello/", HelloServer)
  7. err := http.ListenAndServe(":8080", nil)
  8. if err != nil {
  9. log.Fatal("ListenAndServe: ", err)
  10. }
  11. }

一个go web服务器正常运行起来大概需要以下几个步骤:
+ 创建listen socket,循环监听listen socke
+ accept接受新的链接请求,并创建网络连接conn,然后开启一个goroutine负责处理该链接。
+ 从该链接读取请求参数构造出http.Request对象,然后根据请求路径在路由表中查找,找到对应的上层应用的处理函数,把请求交给应用处理函数。
+ 应用处理函数根据请求的参数等信息做处理,返回不同的信息给用户
+ 应用层处理完该链接请求后关闭该链接(正常流程,如果是http alive则不关闭该链接)

这里面路由表是比较重要的,我们具体分析下http.Server是如何做路由的。
路由表实际上是一个map
key是路径 ==> “/hello”
value是该路径所对应的处理函数 ==> HelloServer

路由表结构

go语言默认的路由表是 ServeMux,结构如下

  1. type ServeMux struct {
  2. mu sync.RWMutex
  3. m map[string]muxEntry //存放具体的路由信息
  4. }
  5. type muxEntry struct {
  6. explicit bool
  7. h Handler
  8. pattern string
  9. }
  10. //muxEntry.Handler是一个接口
  11. type Handler interface {
  12. ServeHTTP(ResponseWriter, *Request)
  13. }
  14. //这边可能会有疑惑
  15. //http.HandleFunc("/hello/", HelloServer)
  16. //helloServer是一个function啊,并没有实现ServeHTTP接口啊
  17. //这是因为虽然我们传入的是一个function,但是HandleFunc会把function转为实现了ServeHTTP接口的一个新类型 HandlerFunc。
  18. /*
  19. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
  20. mux.Handle(pattern, HandlerFunc(handler))
  21. }
  22. type HandlerFunc func(ResponseWriter, *Request)
  23. // ServeHTTP calls f(w, r).
  24. func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
  25. f(w, r)
  26. }
  27. */

路由注册过程

注册过程其实就是往map中插入数据,值得注意的一个地方是如果
注册路径是/tree/并且没有/tree的路由信息,那么会在路由表中自动增加一条/tree的路由,/tree的处理函数是重定向到/tree/。但是如果注册的是/tree是不会自动添加/tree/的路由的

  1. // Handle registers the handler for the given pattern.
  2. // If a handler already exists for pattern, Handle panics.
  3. func (mux *ServeMux) Handle(pattern string, handler Handler) {
  4. mux.mu.Lock()
  5. defer mux.mu.Unlock()
  6. mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
  7. n := len(pattern)
  8. if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
  9. path := pattern
  10. fmt.Printf("redirect for :%s to :%s", pattern, path)
  11. mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern}
  12. }
  13. }

路由查找过程

路由查找过程就是遍历路由表,找到最长匹配请求路径的路由信息并返回,如果找不到返回 NotFoundHandler

  1. func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
  2. mux.mu.RLock()
  3. defer mux.mu.RUnlock()
  4. if h == nil {
  5. h, pattern = mux.match(path)
  6. }
  7. if h == nil {
  8. h, pattern = NotFoundHandler(), ""
  9. }
  10. return
  11. }
  12. func (mux *ServeMux) match(path string) (h Handler, pattern string) {
  13. var n = 0
  14. for k, v := range mux.m {
  15. if !pathMatch(k, path) {
  16. continue
  17. }
  18. //找出匹配度最长的
  19. if h == nil || len(k) > n {
  20. n = len(k)
  21. h = v.h
  22. pattern = v.pattern
  23. }
  24. }
  25. return
  26. }
  27. // 如果路由表中的路径是不以下划线结尾的 /hello
  28. //那么只有请求路径为/hello 完全匹配时才符合
  29. //如果路由表中的注册路径是以下划线结尾的 /hello/
  30. //那么请求路径只要满足/hello/* 就符合该路由
  31. func pathMatch(pattern, path string) bool {
  32. n := len(pattern)
  33. if pattern[n-1] != '/' {
  34. return pattern == path
  35. }
  36. return len(path) >= n && path[0:n] == pattern
  37. }

有疑问加站长微信联系(非本文作者)

本文来自:shanks's blog

感谢作者:shanks

查看原文:http.ServeMux解析

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

16638 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传