官术网_书友最值得收藏!

3.3.6 使用goroutine加載daemon對象并運行

Docker執行完builtins的加載之后,再次回到mainDaemon()的執行流程中。此時,Docker通過一個goroutine協程加載daemon對象并開始運行Docker Server。這一環節的執行,主要包含以下三個步驟:

1)通過init函數中初始化的daemonCfg與eng對象,創建一個daemon對象d。

2)通過daemon對象的Install函數,向eng對象中注冊眾多的處理方法。

3)在Docker Daemon啟動完畢之后,運行名為acceptconnections的Job,主要工作為向init守護進程發送READY=1信號,以便Docker Server開始正常接收請求。

源碼實現位于./docker/docker/docker/daemon.go#L43-L56,如下所示:

go func() {
     d, err := daemon.MainDaemon(daemonCfg, eng)
     if err != nil {
          log.Fatal(err)
     }
     if err := d.Install(eng); err != nil {
          log.Fatal(err)
     }
     if err := eng.Job("acceptconnections").Run(); err != nil {
          log.Fatal(err)
     }
}()

下面詳細分析三個步驟所做的工作。

1.創建daemon對象

daemon.NewDaemon(daemonCfg,eng)是創建daemon對象d的核心部分,主要作用是初始化Docker Daemon的基本環境,如處理config參數,驗證系統支持度,配置Docker工作目錄,設置與加載多種驅動,創建graph環境,驗證DNS配置等。

由于daemon.MainDaemon(daemonCfg,eng)是加載Docker Daemon的核心部分,且篇幅過長,本書第4章將深入分析NewDaemon的實現。

2.通過daemon對象為engine注冊Handler

Docker創建完daemon對象,goroutine立即執行d.Install(eng),具體實現位于./docker/daemon/daemon.go,代碼如下所示:

func (daemon *Daemon) Install(eng *engine.Engine) error {
     for name, method := range map[string]engine.Handler{
          "attach":            daemon.ContainerAttach,
          "build":             daemon.CmdBuild,
          "commit":           daemon.ContainerCommit,
          "container_changes":  daemon.ContainerChanges,
          "container_copy":     daemon.ContainerCopy,
          "container_inspect":   daemon.ContainerInspect,
          "containers":         daemon.Containers,
          "create":            daemon.ContainerCreate,
          "delete":            daemon.ContainerDestroy,
          "export":            daemon.ContainerExport,
          "info":              daemon.CmdInfo,
          "kill":               daemon.ContainerKill,
          ...
          "image_delete":      daemon.ImageDelete, 
     } {
          if err := eng.Register(name, method); err != nil {
               return err
          }
     }
     if err := daemon.Repositories().Install(eng); err != nil {
          return err
     }
     eng.Hack_SetGlobalVar("httpapi.daemon", daemon)
     return nil
}

以上代碼的實現同樣分為三部分:

□ 向eng對象中注冊眾多的處理方法對象。

□ daemon.Repositories().Install(eng)實現了向eng對象注冊多個與Docker鏡像相關的Handler,Install的實現位于./docker/docker/graph/service.go。

□ eng.Hack_SetGlobalVar("httpapi.daemon",daemon)實現向eng對象中類型為map的hack對象中添加一條記錄,鍵為httpapi.daemon,值為daemon。

3.運行名為acceptconnections的Job

Docker在goroutine的最后環節運行名為acceptconnections的Job,主要作用是通知init守護進程,使Docker Daemon開始接受請求。源碼位于./docker/docker/docker/daemon.go#L53-L55,如下所示:

// after the daemon is done setting up we can tell the api to start
// accepting connections
if err := eng.Job("acceptconnections").Run(); err != nil {
     log.Fatal(err)
}

關于Job的運行流程大同小異,總結而言,都是首先創建特定名稱的Job,其次為Job配置環境參數,最后運行Job對應Handler的函數。作為本書涉及的第一個具體Job,下面將對acceptconnections這個Job的執行進程深入分析。

eng.Job("acceptconnections").Run()的運行包含兩部分:首先執行eng.Job("acceptconnections"),返回一個Job實例,隨后再執行該Job實例的Run()函數。

eng.Job("acceptconnections")的實現位于./docker/docker/engine/engine.go#L115-L137,如下所示:

func (eng *Engine) Job(name string, args ...string) *Job {
     job := &Job{
          Eng:    eng,
          Name:   name,
          Args:   args,
          Stdin:  NewInput(),
          Stdout: NewOutput(),
          Stderr: NewOutput(),
          env:    &Env{},
     }
     if eng.Logging {
          job.Stderr.Add(utils.NopWriteCloser(eng.Stderr))
     }
     if handler, exists := eng.handlers[name]; exists {
          job.handler = handler
     } else if eng.catchall != nil && name != "" {
          job.handler = eng.catchall
     }
     return job
}

通過分析以上創建Job的源碼,我們可以發現Docker首先創建一個類型為Job的job對象,該對象中Eng屬性為函數的調用者eng,該對象的Name屬性為acceptconnections,沒有其他參數傳入。另外在eng對象所有的handlers屬性中尋找key為acceptconnections所對應的value值(即具體的Handler)。由于在加載builtins時,源碼remote(eng)已經向eng注冊過這樣一條記錄,鍵為acceptconnections,值為apiserver.AcceptConnections。因此,Job對象的handler屬性為apiserver.AcceptConnections。最后函數返回已經初始化完畢的對象Job。

創建完Job對象之后,隨即執行該job對象的run()函數。run()函數的源碼實現位于./docker/docker/engine/job.go#L48-L96,該函數執行指定的Job,并在Job執行完成前一直處于阻塞狀態。對于名為acceptconnections的Job對象,運行代碼為job.status=job.handler(job),由于job.handler值為apiserver.AcceptConnections,故真正執行的是job.status=apiserver.AcceptConnections(job)。

AcceptConnections的具體實現屬于Docker Server的范疇,深入研究Docker Server可以發現,這部分源碼位于./docker/docker/api/server/server.go#L1370-L1380,如下所示:

func AcceptConnections(job *engine.Job) engine.Status {
     // Tell the init daemon we are accepting requests
     go systemd.SdNotify("READY=1")
     if activationLock != nil {
          close(activationLock)
     }
     return engine.StatusOK
}

AcceptConnections函數的重點是go systemd.SdNotify("READY=1")的實現,位于./docker/docker/pkg/system/sdnotify.go#L12-L33,主要作用是通知init守護進程Docker Daemon的啟動已經全部完成,潛在的功能是要求Docker Daemon開始接收并服務Docker Client發送來的API請求。

至此,通過goroutine來加載daemon對象并運行啟動Docker Server的工作全部完成。

主站蜘蛛池模板: 平塘县| 日喀则市| 永仁县| 郁南县| 无锡市| 平陆县| 镇巴县| 即墨市| 武穴市| 大邑县| 汾阳市| 霍州市| 通江县| 凌源市| 青阳县| 清远市| 都安| 威海市| 通道| 睢宁县| 林甸县| 南乐县| 卫辉市| 玉田县| 桦川县| 万荣县| 诏安县| 东港市| 元氏县| 汽车| 安龙县| 淳化县| 交口县| 菏泽市| 缙云县| 香港| 周口市| 离岛区| 衢州市| 林芝县| 河曲县|