(2) 那我们通过docker客户端发送一个命令,docker是怎样接收到并处理的呢,我们就举个例子来看一下,比如docker pull 命令;

我们回到 docker/docker.go 中,在上一章中我们讲了docker daemon的启动,代码讲到了handleGlobalDaemonFlag()的位置。我们接着继续看:

c := cli.New(clientCli, daemonCli)
if err := c.Run(flag.Args()...); err != nil {
    if sterr, ok := err.(cli.StatusError); ok {
        if sterr.Status != "" {
            fmt.Fprintln(os.Stderr, sterr.Status)
            os.Exit(1)
        }
        os.Exit(sterr.StatusCode)
    }
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
}

 

cli 在cli/cli.go文件中,

func New(handlers ...Handler) *Cli {

    // make the generic Cli object the first cli handler

    // in order to handle `docker help` appropriately

    cli := new(Cli)

    cli.handlers = append([]Handler{cli}, handlers...)

    return cli

}

这里的handler就是各种处理方法的结合;

然后是c.Run()方法,Run方法是调用了command方法,实质就是通过反射机制在handlers中找到args中名称相同的命令来执行:

func (cli *Cli) command(args ...string) (func(...string) error, error) {

    for _, c := range cli.handlers {

        if c == nil {

            continue

        }

        camelArgs := make([]string, len(args))

        for i, s := range args {

            if len(s) == 0 {

                return nil, errors.New("empty command")

            }

            camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])

        }

        methodName := "Cmd" + strings.Join(camelArgs, "")

        method := reflect.ValueOf(c).MethodByName(methodName)

        if method.IsValid() {

            // type assert

            if c, ok := c.(Initializer); ok {

                if err := c.Initialize(); err != nil {

                    return nil, initErr{err}

                }

            }

            return method.Interface().(func(...string) error), nil

        }

    }

    return nil, errors.New("command not found")

}

比如我们运行docker pull,实际去查找的命令就是CmdPull。

pull命令的实现是在 api/client 下面,这个目录下还有其他命令,我们只举pull的例子来看:

pull.go中最主要的一句话是

_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")

clientRequestAttemptLogin在同目录的utils.go目录下;

POST 是方法, /images.create? 这个是url,回想起上一章我们在apiserver(api/server/server.go)中看到的, 这url对应的是 s.postImagesCreate

postImagesCreate的定义在api/server/image.go 中;

 

现在大致了解了docker的c/s架构和docker客户端怎样给docker daemon发命令;下一篇来分析一下docker daemon的启动过程,也就是daemon.NewDaemon(cli.Config, registryService)函数的实现过程;

因为docker daemon的启动过程涉及很多操作,包括网络环境初始化,存储初始化等等,先了解一下daemon的总体启动过程,然后再分每一项各个击破;