实现一个简单的golangweb服务器

  • web服务本质上讲就是根据不同的请求给出不同的响应

  • 一种请求对应某一种处理函数

  • 对于请求的区分(根据请求的路径,请求的方法,请求的参数)

    下面探讨一种针对请求参数的某个字段来区分具体的请求的web服务器,假定客户端与服务器间通过json交互,其中请求中有字段handler,此字段用于指定服务端具体的handler服务。

  • 首先:

    定义若干的服务函数HandlerFunc func(ResponseWriter, *Request),如:

func Test(w http.ResponseWriter, r *http.Request) {
            w.Write([]byte("hello"))
        }
  • 其次:将这个处理函数都放进一个map[string]*httpTaskHandle,这个map维护了服务函数名与服务函数之间的映射关系(在web服务启动之前,此map应该被初始化好)
  • 统一的入口:http.HandleFunc("/", MainHandler),MainHandler为整个服务的入口,MainHandler收到请求后,将请求数据进行解析,获取到handler参数,根据上面的map拿到具体的处理函数如Test,将请求上下文交给Test完成此次服务的处理。
  • 这里的HandleFunc函数本质是ServeMux类的一个方法。ServeMux是整个web的入口,由ServeMux调用具体的ServeHTTP进行处理,而ServeHTTP内部则是根据注册于mux.m的处理规则进行正则匹配,从而获取处理函数。这里ServerMux是根据请求路径r.URL.Path,去匹配具体的处理函数的。
type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {
    h       Handler
    pattern string
}

​ 回顾我们上面关于MainHandler的处理方法,可以看出,我们只用了其根路由"/",具体的处理函数,由MainHandler根据参数handler的解析结果交给具体的处理函数了。

  • 串起来梳理整个请求的处理过程
1. http.HandleFunc("/",MainHandler)将"/"路径处理的函数注册进来,注册于DefaultServeMux中的m
2. 通过ListenAndServe来开启本地端口监听
3. 当有请求过来,开启一个go程进行服务
4. go程中读取请求内容,实例化serverHandler,并调用ServeHTTP处理
5. ServerHTTP,根据serverHandler中调用Handler的ServeHTTP具体实现
6. 在调用具体的ServeHTTP,如果没有handler那么则使用DefaultServer
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)
}
7. http.HandleFunc("/", MainHandler)而上面这个统一的入口则是对DefaultServeMux的初始化
8. 因此,最终所有的request请求,都会落到MainHandler中
9. 假设具体的请求体中的json解析完成,字段handler为Test(这里的handler并不是golang框架的handler,是自己json中的一个字段,用来指定服务服务接口的)
10. 调用Test处理器,进行处理
  • 借用一位牛人的图(此图出自--简书,人世间)