写过应用框架的都应该知道有个路由模块,来看看docker的http请求是怎么路由的
docker的server.go文件中,首先入口是New的时候
func New(cfg *Config) *Server { //需要创建Server的实例,因为命令的具体执行都是Server对象的函数 srv := &Server{ cfg: cfg, start: make(chan struct{}), } //这里就是创建Router的操作 r := createRouter(srv) srv.router = r return srv }
接下来看看createRouter的具体执行吧
func createRouter(s *Server) *mux.Router { r := mux.NewRouter() //Router依赖第三包,地址:github.com/gorilla/mux if os.Getenv("DEBUG") != "" { profilerSetup(r, "/debug/") } //map->map[string]HTTPAPIFunc m := map[string]map[string]HTTPAPIFunc{ "HEAD": { //key,value->server对应的处理函数 "/containers/{name:.*}/archive": s.headContainersArchive, }, "GET": { "/_ping": s.ping, "/events": s.getEvents, "/info": s.getInfo, "/version": s.getVersion, "/images/json": s.getImagesJSON, "/images/search": s.getImagesSearch, "/images/get": s.getImagesGet, "/images/{name:.*}/get": s.getImagesGet, "/images/{name:.*}/history": s.getImagesHistory, "/images/{name:.*}/json": s.getImagesByName, "/containers/ps": s.getContainersJSON, "/containers/json": s.getContainersJSON, "/containers/{name:.*}/export": s.getContainersExport, "/containers/{name:.*}/changes": s.getContainersChanges, "/containers/{name:.*}/json": s.getContainersByName, "/containers/{name:.*}/top": s.getContainersTop, "/containers/{name:.*}/logs": s.getContainersLogs, "/containers/{name:.*}/stats": s.getContainersStats, "/containers/{name:.*}/attach/ws": s.wsContainersAttach, "/exec/{id:.*}/json": s.getExecByID, "/containers/{name:.*}/archive": s.getContainersArchive, "/volumes": s.getVolumesList, "/volumes/{name:.*}": s.getVolumeByName, }, "POST": { "/auth": s.postAuth, "/commit": s.postCommit, "/build": s.postBuild, "/images/create": s.postImagesCreate, "/images/load": s.postImagesLoad, "/images/{name:.*}/push": s.postImagesPush, "/images/{name:.*}/tag": s.postImagesTag, "/containers/create": s.postContainersCreate, "/containers/{name:.*}/kill": s.postContainersKill, "/containers/{name:.*}/pause": s.postContainersPause, "/containers/{name:.*}/unpause": s.postContainersUnpause, "/containers/{name:.*}/restart": s.postContainersRestart, "/containers/{name:.*}/start": s.postContainersStart, "/containers/{name:.*}/stop": s.postContainersStop, "/containers/{name:.*}/wait": s.postContainersWait, "/containers/{name:.*}/resize": s.postContainersResize, "/containers/{name:.*}/attach": s.postContainersAttach, "/containers/{name:.*}/copy": s.postContainersCopy, "/containers/{name:.*}/exec": s.postContainerExecCreate, "/exec/{name:.*}/start": s.postContainerExecStart, "/exec/{name:.*}/resize": s.postContainerExecResize, "/containers/{name:.*}/rename": s.postContainerRename, "/volumes": s.postVolumesCreate, }, "PUT": { "/containers/{name:.*}/archive": s.putContainersArchive, }, "DELETE": { "/containers/{name:.*}": s.deleteContainers, "/images/{name:.*}": s.deleteImages, "/volumes/{name:.*}": s.deleteVolumes, }, "OPTIONS": { "": s.optionsHandler, }, } // If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*" // otherwise, all head values will be passed to HTTP handler corsHeaders := s.cfg.CorsHeaders if corsHeaders == "" && s.cfg.EnableCors { corsHeaders = "*" } for method, routes := range m { for route, fct := range routes { logrus.Debugf("Registering %s, %s", method, route) localRoute := route //例如_ping localFct := fct //例如Server.ping函数 localMethod := method //例如"GET" // build the handler function //golang的HTTPHandler f := makeHTTPHandler(s.cfg.Logging, localMethod, localRoute, localFct, corsHeaders, version.Version(s.cfg.Version)) // 这里就是route的创建 if localRoute == "" { r.Methods(localMethod).HandlerFunc(f) } else { r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f) r.Path(localRoute).Methods(localMethod).HandlerFunc(f) } } }
再去看看mux的例子说明:
//They are defined using the format {name} or {name:pattern}. //If a regular expression pattern is not // defined, the matched variable will be anything until the next slash r := mux.NewRouter() r.HandleFunc("/products/{key}", ProductHandler) r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler
最终mux执行的golang http handler的interface ServeHTTP
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Clean path to canonical form and redirect. if p := cleanPath(req.URL.Path); p != req.URL.Path { // Added 3 lines (Philip Schlump) - It was droping the query string and #whatever from query. // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: // http://code.google.com/p/go/issues/detail?id=5252 url := *req.URL url.Path = p p = url.String() w.Header().Set("Location", p) w.WriteHeader(http.StatusMovedPermanently) return } var match RouteMatch var handler http.Handler if r.Match(req, &match) { handler = match.Handler //这里是关键步骤 setVars(req, match.Vars) setCurrentRoute(req, match.Route) } if handler == nil { handler = r.NotFoundHandler if handler == nil { handler = http.NotFoundHandler() } } if !r.KeepContext { defer context.Clear(req) } handler.ServeHTTP(w, req) //这里最终执行回调的匿名函数 }