<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC=3] ## 1.Docker Server簡介 Docker架構中,Docker Server是Docker Daemon的重要組成部分。Docker Server最主要的功能是:接受用戶通過Docker Client發送的請求,并按照相應的路由規則實現路由分發。 同時,Docker Server具備十分優秀的用戶友好性,多種通信協議的支持大大降低Docker用戶使用Docker的門檻。除此之外,Docker Server設計實現了詳盡清晰的API接口,以供Docker用戶選擇使用。通信安全方面,Docker Server可以提供安全傳輸層協議(TLS),保證數據的加密傳輸。并發處理方面,Docker Daemon大量使用了Golang中的goroutine,大大提高了服務端的并發處理能力。 本文為《Docker源碼分析》系列的第五篇——Docker Server的創建。 ## 2\. Docker Server源碼分析內容安排 本文將從源碼的角度分析Docker Server的創建,分析內容的安排主要如下: (1) “serveapi”這個job的創建并執行流程,代表Docker Server的創建; (2) “serveapi”這個job的執行流程深入分析; (3) Docker Server創建Listener并服務API的流程分析。 ## 3.Docker Server創建流程 [《Docker源碼分析(三):Docker Daemon啟動》](http://www.infoq.com/cn/articles/docker-source-code-analysis-part3)主要分析了Docker Daemon的啟動,而在mainDaemon()運行的最后環節,實現了創建并運行名為”serveapi”的job。這一環節的作用是:讓Docker Daemon提供API訪問服務。實質上,這正是實現了Docker架構中Docker Server的創建與運行。 從流程的角度來說,Docker Server的創建并運行,代表了”serveapi”這個job的整個生命周期:創建Job實例job,配置job環境變量,以及最終執行該job。本章分三節具體分析這三個不同的階段。 ### 3.1創建名為”serveapi”的job Job是Docker架構中Engine內部最基本的任務執行單位,故創建Docker Server這一任務的執行也不例外,需要表示為一個可執行的Job。換言之,需要創建Docker Server,則必須創建一個相應的Job。具體的Job創建形式位于[./docker/docker/daemon.go](https://github.com/docker/docker/blob/v1.2.0/docker/daemon.go#L66),如下: ~~~ job := eng.Job("serveapi", flHosts...) ~~~ 以上代碼通過Engine實例eng創建一個Job類型的實例job,job名為”serveapi”,同時用flHost的值來初始化job.Args。flHost的作用是:配置Docker Server監聽的協議與監聽的地址。 需要注意的是,《Docker源碼分析(三):Docker Daemon啟動》mainDaemon()具體實現過程中,在加載builtins環節已經向eng對象注冊了key為”serveapi”的Handler,而該Handler的value為api.ServeApi。因此,在運行名為”serveapi”的job時,會執行該job的Handler,即api.ServeApi。 ### 3.2配置job環境變量 創建完Job實例job之后,Docker Daemon為job配置環境參數。在Job實現過程中,為Job配置參數有兩種方式:第一,創建Job實例時,用指定參數直接初始化Job的Args屬性;第二,創建完Job后,給Job添加指定的環境變量。以下代碼則實現了為創建的job配置環境變量: ~~~ job.SetenvBool("Logging", true) job.SetenvBool("EnableCors", *flEnableCors) job.Setenv("Version", dockerversion.VERSION) job.Setenv("SocketGroup", *flSocketGroup) job.SetenvBool("Tls", *flTls) job.SetenvBool("TlsVerify", *flTlsVerify) job.Setenv("TlsCa", *flCa) job.Setenv("TlsCert", *flCert) job.Setenv("TlsKey", *flKey) job.SetenvBool("BufferRequests", true) ~~~ 對于以上配置,環境變量的歸納總結如下表: | 環境變量名 | flag參數 | 默認值 | 作用值 | |---|---|---|---| | Logging | | true | 使用日志輸出 | | EnableCors | flEnableCors | false | 在遠程API中提供CORS頭 | | Version | | | 顯示Docker版本號 | | SocketGroup | flSocketGroup | “docker” | 在daemon模式中unix domain socket分配用戶組名 | | Tls | flTls | false | 使用TLS安全傳輸協議 | | TlsVerify | flTlsVerify | false | 使用TLS并驗證遠程Client | | TlsCa | flCa | | 指定CA文件路徑 | | TlsCert | flCert | | TLS證書文件路徑 | | TlsKey | flKey | | TLS密鑰文件路徑 | | BufferRequest | | true | 緩存Docker Client請求 | ### 3.3 運行job 配置完畢job的環境變量,隨即執行job的運行函數,具體實現代碼如下: ~~~ if err := job.Run(); err != nil { log.Fatal(err) } ~~~ 在eng對象中已經注冊過key為”serveapi”的Handler,故在運行job的時候,執行這個Handler的value值,相應Handler的value為api.ServeApi。至此,名為”serveapi”的job的生命周期已經完備。下文將深入分析job的Handler,api.ServeApi執行細節的具體實現。 ## 4.ServeApi運行流程 本章將深入分析Docker Server提供API服務的部分,從源碼的角度剖析Docker Server的架構設計與實現。 作為一個監聽請求、處理請求的服務端,Docker Server首先明確自身需要為多少種通信協議提供服務,在Docker這個C/S模式的架構中,可以使用的協議無外乎三種:TCP協議,Unix Socket形式,以及fd的形式。隨后,Docker Server根據協議的不同,分別創建不同的服務端實例。最后,在不同的服務端實例中,創建相應的路由模塊,監聽模塊,以及處理請求的Handler,形成一個完備的server。 ”serveapi”這個job在運行時,將執行api.ServeApi函數。ServeApi的功能是:循環檢查所有Docker Daemon當前支持的通信協議,并對于每一種協議都創建一個goroutine,在這個goroutine內部配置一個服務于HTTP請求的server端。ServeApi的代碼實現位于[./docker/api/server/server.go#L1339](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1339-L1368): 第一,判斷job.Args的長度是否為0,由于通過flHosts來初始化job.Args,故job.Args的長度若為0的話,說明沒有Docker Server沒有監聽的協議與地址,參數有誤,返回錯誤信息。代碼如下: ~~~ if len(job.Args) == 0 { return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name) } ~~~ 第二,定義兩個變量,protoAddrs代表flHosts的內容;而chError定義了和protoAddrs長度一致的error類型channel管道,chError的作用在下文中會說明。同時還定義了activationLock,這是一個用來同步”serveapi”和”acceptconnections”這兩個job執行的channel。在serveapi運行時ServeFd和ListenAndServe的實現中,由于activationLock這個channel中沒有內容而阻塞,而當運行”acceptionconnections”這個job時,會首先通知init進程Docker Daemon已經啟動完畢,并關閉activationLock,同時也開啟了serveapi的繼續執行。正是由于activationLock的存在,保證了”acceptconnections”這個job的運行起到通知”serveapi”開啟正式服務于API的效果。代碼如下: ~~~ var ( protoAddrs = job.Args chErrors = make(chan error, len(protoAddrs)) ) activationLock = make(chan struct{}) ~~~ 第三,遍歷protoAddrs,即job.Args,將其中的每一項都按照字符串“://”進行分割,若分割后protoAddrParts的長度不為2,則說明協議加地址的書寫形式有誤,返回job錯誤;若不為2,則分割獲得每一項中的協議protoAddrPart[0]與地址protoAddrParts[1]。最后分別創建一個goroutine來執行ListenAndServe的操作。goroutine的運行主要依賴于ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)的運行結果,若返回error,則chErrors中有error,當前goroutine執行完畢;若沒有返回error,則該goroutine持續運行,持續提供服務。其中最為重要的是ListenAndServe的實現,該函數具體實現了如何創建listener、router以及server,并協調三者進行工作,最終服務于API請求。代碼如下: ~~~ for _, protoAddr := range protoAddrs { protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name) } go func() { log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job) }() } ~~~ 第四,根據chErrors的值運行,若chErrors這個channel中有錯誤內容,則ServeApi該函數返回;若無錯誤內容,則循環被阻塞。代碼如下: ~~~ for i := 0; i < len(protoAddrs); i += 1 { err := <-chErrors if err != nil { return job.Error(err) } } return engine.StatusOK ~~~ 至此, ServeApi的運行流程已經詳細分析完畢,其中核心部分ListenAndServe的實現,下一章開始深入。 ## 5.ListenAndServe實現 ListenAndServe的功能是:使Docker Server監聽某一指定地址,接受該地址上的請求,并對以上請求路由轉發至相應的處理函數Handler處。從實現的角度來看,ListenAndServe主要實現了設置一個服務于HTTP的server,該server將監聽指定地址上的請求,并對請求做特定的協議檢查,最終完成請求的路由與分發。代碼實現位于[./docker/api/server/server.go](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1247-L1335)。 ListenAndServe的實現可以分為以下4個部分: (1) 創建router路由實例; (2) 創建listener監聽實例; (3) 創建http.Server; (4) 啟動API服務。 ListenAndServe的執行流程如下圖: ![](https://box.kancloud.cn/2015-11-12_56443b098334c.jpg) 圖5.1 ListenAndServer執行流程圖 下文將按照ListenAndServe執行流程圖一一深入分析各個部分。 ### 5.1 創建router路由實例 首先,ListenAndServe的實現中通過createRouter創建了一個router路由實例。代碼實現如下: ~~~ rr, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version")) if err != nil { return err } ~~~ createRouter的實現位于[./docker/api/server/server.go#L1094](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1094-L1171)。 創建router路由實例是一個重要的環節,路由實例的作用是:負責Docker Server對請求進行路由以及分發。實現過程中,主要兩個步驟:第一,創建全新的router路由實例;第二,為router實例添加路由記錄。 #### **5.1.1** **創建空路由實例** 實質上,createRouter通過包gorilla/mux實現了一個功能強大的路由器和分發器。如下: ~~~ r := mux.NewRouter() ~~~ NewRouter()函數返回了一個全新的router實例r。在創建Router實例時,給Router對象的兩個屬性進行賦值,這兩個屬性為nameRoutes和KeepContext。其中namedRoutes屬性為一個map類型,其中key為string類型,value為Route路由記錄類型;另外,KeepContext屬性為false,表示Docker Server在處理完請求之后,就清除請求的內容,不對請求做存儲操作。代碼位于[./docker/vendor/src/github.com/gorilla/mux/mux.go#L16](https://github.com/docker/docker/blob/v1.2.0/vendor/src/github.com/gorilla/mux/mux.go#L16-L18),如下: ~~~ func NewRouter() *Router { return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} } ~~~ 可見,以上代碼返回的類型為mux.Router。mux.Router會通過一系列已經注冊過的路由記錄,來為接受的請求做匹配,首先通過請求的URL或者其他條件,找到相應的路由記錄,并調用這條路由記錄中的執行Handler。mux.Router有以下這些特性: * 請求可以基于URL 的主機名、路徑、路徑前綴、shemes、請求頭和請求值、HTTP請求方法類型或者使用自定義的匹配規則; * URL主機名和路徑可以擁有一個正則表達式來表示; * 注冊的URL可以被直接運用,也可以被保留,這樣可以保證維護資源的使用; * 路由記錄可以被用以子路由器:如果父路由記錄匹配,則嵌套記錄只會被用來測試。當設計一個組內的路由記錄共享相同的匹配條件時,如主機名、路勁前綴或者其他重復的屬性,子路由的方式很有幫助; * mux.Router實現了http.Handler接口,故和標準的http.ServeMux兼容。 #### **5.1.2** **添加路由記錄** Router路由實例r創建完畢,下一步工作是為Router實例r添加所需要的路由記錄。路由記錄存儲著用來匹配請求的信息,包括對請求的匹配規則,以及匹配之后的Handler執行入口。 回到[createRouter實現代碼](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1094-L1171)中,首先判斷Docker Daemon的啟動過程中有沒有開啟DEBUG模式。通過docker可執行文件啟動Docker Daemon,解析flag參數時,若flDebug的值為false,則說明不需要配置DEBUG環境;若flDebug的值為true,則說明需要為Docker Daemon添加DEBUG功能。具體的代碼實現如下: ~~~ if os.Getenv("DEBUG") != "" { AttachProfiler(r) } ~~~ AttachProiler(r)的功能是為路由實例r添加與DEBUG相關的路由記錄,具體實現位于[./docker/api/server/server.go#L1083](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1083-L1092),如下: ~~~ func AttachProfiler(router *mux.Router) { router.HandleFunc("/debug/vars", expvarHandler) router.HandleFunc("/debug/pprof/", pprof.Index) router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) router.HandleFunc("/debug/pprof/profile", pprof.Profile) router.HandleFunc("/debug/pprof/symbol", pprof.Symbol) router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP) router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP) router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP) } ~~~ 分析以上源碼,可以發現Docker Server使用兩個包來完成DEBUG相關的工作:expvar和pprof。包expvar為公有變量提供標準化的接口,使得這些公有變量可以通過HTTP的形式在”/debug/vars”這個URL下被訪問,傳輸時格式為JSON。包pprof將Docker Server運行時的分析數據通過”/debug/pprof/”這個URL向外暴露。這些運行時信息包括以下內容:可得的信息列表、正在運行的命令行信息、CPU信息、程序函數引用信息、ServeHTTP這個函數三部分信息使用情況(堆使用、goroutine使用和thread使用)。 回到[createRouter函數實現](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1094-L1171)中,完成DEBUG功能的所有工作之后,Docker Docker創建了一個map類型的對象m,用于初始化路由實例r的路由記錄。簡化的m對象,代碼如下: ~~~ m := map[string]map[string]HttpApiFunc{ "GET": { …… "/images/{name:.*}/get": getImagesGet, …… }, "POST": { …… "/containers/{name:.*}/copy": postContainersCopy, }, "DELETE": { "/containers/{name:.*}": deleteContainers, "/images/{name:.*}": deleteImages, }, "OPTIONS": { "": optionsHandler, }, } ~~~ 對象m的類型為map,其中key為string類型,代表HTTP的請求類型,如”GET”,”POST”,”DELETE”等,value為另一個map類型,該map代表的是URL與執行Handler的映射。在第二個map類型中,key為string類型,代表是的請求URL,value為HttpApiFunc類型,代表具體的執行Handler。其中HttpApiFunc類型的定義如下: ~~~ type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error ~~~ 完成對象m的定義,隨后Docker Server通過該對象m來添加路由實例r的路由記錄。對象m的請求方法,請求URL和請求處理Handler這三樣內容可以為對象r構建一條路由記錄。實現代碼。如下: ~~~ for method, routes := range m { for route, fct := range routes { log.Debugf("Registering %s, %s", method, route) localRoute := route localFct := fct localMethod := method f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, version.Version(dockerVersion)) 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) } } } ~~~ 以上代碼,在第一層循環中,按HTTP請求方法劃分,獲得請求方法各自的路由記錄,第二層循環,按匹配請求的URL進行劃分,獲得與URL相對應的執行Handler。在嵌套循環中,通過makeHttpHandler返回一個執行的函數f。在返回的這個函數中,涉及了logging信息,CORS信息(跨域資源共享協議),以及版本信息。以下舉例說明makeHttpHandler的實現,從對象m可以看到,對于”GET”請求,請求URL為”/info”,則請求Handler為”getInfo”。執行makeHttpHandler的具體代碼實現如下: ~~~ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // log the request log.Debugf("Calling %s %s", localMethod, localRoute) if logging { log.Infof("%s %s", r.Method, r.RequestURI) } if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") { userAgent := strings.Split(r.Header.Get("User-Agent"), "/") if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) { log.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion) } } version := version.Version(mux.Vars(r)["version"]) if version == "" { version = api.APIVERSION } if enableCors { writeCorsHeaders(w, r) } if version.GreaterThan(api.APIVERSION) { http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound) return } if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil { log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err) httpError(w, err) } } } ~~~ 可見makeHttpHandler的執行直接返回一個函數func(w http.ResponseWriter, r *http.Request) 。在這個func函數的實現中,判斷makeHttpHandler傳入的logging參數,若為true,則將該Handler的執行通過日志顯示,另外通過makeHttpHandler傳入的enableCors參數判斷是否在HTTP請求的頭文件中添加跨域資源共享信息,若為true,則通過writeCorsHeaders函數向response中添加有關CORS的HTTP Header,代碼實現位于[./docker/api/server/server.go#L1022](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1022-L1026),如下: ~~~ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { w.Header().Add("Access-Control-Allow-Origin", "*") w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") } ~~~ 最為重要的執行部分位于handlerFunc(eng, version, w, r, mux.Vars(r)),如以下代碼: ~~~ if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil { log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err) httpError(w, err) } ~~~ 對于”GET”請求類型,”/info”請求URL的請求,由于Handler名為getInfo,也就是說handlerFunc這個形參的值為getInfo,故執行部分直接運行getInfo(eng, version, w, r, mux.Vars(r)),而getInfo的具體實現位于[./docker/api/server/serve.go#L269](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L269-L273),如下: ~~~ func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { w.Header().Set("Content-Type", "application/json") eng.ServeHTTP(w, r) return nil } ~~~ 以上makeHttpHandler的執行已經完畢,返回func函數,作為指定URL對應的執行Handler。 創建完處理函數Handler,需要向路由實例中添加新的路由記錄。如果URL信息為空,則直接為該HTTP請求方法類型添加路由記錄;若URL不為空,則為請求URL路徑添加新的路由記錄。需要額外注意的是,在URL不為空,為路由實例r添加路由記錄時,考慮了API版本的問題,通過r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)來實現。 至此,mux.Router實例r的兩部分工作工作已經全部完成:創建空的路由實例r,為r添加相應的路由記錄,最后返回路由實例r。 現I時er路由記錄。需要額外的利次循環中,都有不同的組合1083lla/mux/mux.go, ### 5.2 創建listener監聽實例 路由模塊,完成了請求的路由與分發這一重要部分,屬于[ListenAndServe實現](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1247-L1335)中的第一個重要工作。對于請求的監聽功能,同樣需要模塊來完成。而在[ListenAndServe實現](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1247-L1335)中,第二個重要的工作就是創建Listener。Listener是一種面向流協議的通用網絡監聽模塊。 在創建Listener之前,先判斷Docker Server允許的協議,若協議為fd形式,則直接通過ServeFd來服務請求;若協議不為fd形式,則繼續往下執行。 在程序執行過程中,需要判斷”serveapi”這個job的環境中”BufferRequests”的值,是否為真,若為真,則通過包listenbuffer創建一個Listener的實例l,否則的話直接通過包net創建Listener實例l。具體的代碼位于[./docker/api/server/server.go#L1269](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1269-L1273),如下: ~~~ if job.GetenvBool("BufferRequests") { l, err = listenbuffer.NewListenBuffer(proto, addr, activationLock) } else { l, err = net.Listen(proto, addr) } ~~~ 由于在mainDaemon()中創建”serveapi”這個job之后,給job添加環境變量時,已經給”BufferRequets”賦值為true,故使用包listenbuffer創建listener實例。 Listenbuffer的作用是:讓Docker Server可以立即監聽指定協議地址上的請求,但是將這些請求暫時先緩存下來,等Docker Daemon全部啟動完畢之后,才讓Docker Server開始接受這些請求。這樣設計有一個很大的好處,那就是可以保證在Docker Daemon還沒有完全啟動完畢之前,接收并緩存盡可能多的用戶請求。 若協議的類型為TCP,另外job中環境變量Tls或者TlsVerify有一個為真,則說明Docker Server需要支持HTTPS服務,需要為Docker Server配置安全傳輸層協議(TLS)的支持。為實現TLS協議,首先需要建立一個tls.Config類型實例tlsConfig,然后在tlsConfig中加載證書,認證信息等,最終通過包tls中的NewListener函數,創建出適應于接收HTTPS協議請求的Listener實例l,代碼如下: ~~~ l = tls.NewListener(l, tlsConfig) ~~~ 至此,創建網絡監聽的Listener部分已經全部完成。 ### 5.3 創建http.Server Docker Server同樣需要創建一個Server對象來運行HTTP服務端。在[ListenAndServe實現](https://github.com/docker/docker/blob/v1.2.0/api/server/server.go#L1247-L1335)中第三個重要的工作就是創建http.Server: ~~~ httpSrv := http.Server{Addr: addr, Handler: r} ~~~ 其中addr為需要監聽的地址,r為mux.Router路由實例。 ### 5.4 啟動API服務 創建http.Server實例之后,Docker Server立即啟動API服務,使Docker Server開始在Listener監聽實例l上接受請求,并對于每一個請求都生成一個新的goroutine來做專屬服務。對于每一個請求,goroutine會讀取請求,查詢路由表中的路由記錄項,找到匹配的路由記錄,最終調用路由記錄中的執行Handler,執行完畢后,goroutine對請求返回響應信息。代碼如下: ~~~ return httpSrv.Serve(l) ~~~ 至此,ListenAndServer的所有流程已經分析完畢,Docker Server已經開始針對不同的協議,服務API請求。 ## 6.總結 Docker Server作為Docker Daemon架構中請求的入口,接管了所有Docker Daemon對外的通信。通信API的規范性,通信過程的安全性,服務請求的并發能力,往往都是Docker用戶最為關心的內容。本文基于源碼,分析了Docker Server大部分的細節實現。希望Docker用戶可以初探Docker Server的設計理念,并且可以更好的利用Docker Server創造更大的價值。 ## 7.作者簡介 孫宏亮,[DaoCloud](http://www.daocloud.io/)初創團隊成員,軟件工程師,浙江大學VLIS實驗室應屆研究生。讀研期間活躍在PaaS和Docker開源社區,對Cloud Foundry有深入研究和豐富實踐,擅長底層平臺代碼分析,對分布式平臺的架構有一定經驗,撰寫了大量有深度的技術博客。2014年末以合伙人身份加入DaoCloud團隊,致力于傳播以Docker為主的容器的技術,推動互聯網應用的容器化步伐。郵箱:[allen.sun@daocloud.io](mailto:allen.sun@daocloud.io) ## 8.參考文獻 [http://guzalexander.com/2013/12/06/golang-channels-tutorial.html](http://guzalexander.com/2013/12/06/golang-channels-tutorial.html) [http://www.gorillatoolkit.org/pkg/mux](http://www.gorillatoolkit.org/pkg/mux) [http://docs.studygolang.com/pkg/expvar/](http://docs.studygolang.com/pkg/expvar/) [http://docs.studygolang.com/pkg/net/http/pprof/](http://docs.studygolang.com/pkg/net/http/pprof/)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看