VMC源碼的簡單分析

簡單看了下VMC的源碼,寫一個類似提綱的東西。文章裏面主要包括VMC各個代碼塊和他們實現的功能,通過一個命令的實現流程來分析。

VMC執行一條命令的流程:

1. bin/vmc

vmc是一個gem包,所以我們可以在自己的gem文件夾下面找到他們,執行vmc命令其實就是運行bin目錄下的vmc文件。這個文件很簡單,首先require lib/cli.rb,然後調用VMC::Cli::Runner.run。

2. Cli.rb

前面說到vmc require了Cli.rb文件,而Cli.rb包含了幾乎所有需要用到的模塊,包括micro的以及普通的Cli。由於我沒用過micro的命令,所以本文不對micro作出分析。我們看下Cli.rb文件中有關Cli的model(代碼中的autoload相當於lazy的require,只有在需要時才require):

  module Cli
    autoload :Config,         "#{ROOT}/cli/config"
    autoload :Framework,      "#{ROOT}/cli/frameworks"
    autoload :Runner,         "#{ROOT}/cli/runner"
    autoload :ZipUtil,        "#{ROOT}/cli/zip_util"
    autoload :ServicesHelper, "#{ROOT}/cli/services_helper"
    autoload :TunnelHelper,   "#{ROOT}/cli/tunnel_helper"
    autoload :ManifestHelper, "#{ROOT}/cli/manifest_helper"
    autoload :ConsoleHelper,  "#{ROOT}/cli/console_helper"
 
    module Command
      autoload :Base,         "#{ROOT}/cli/commands/base"
      autoload :Admin,        "#{ROOT}/cli/commands/admin"
      autoload :Apps,         "#{ROOT}/cli/commands/apps"
      autoload :Micro,        "#{ROOT}/cli/commands/micro"
      autoload :Misc,         "#{ROOT}/cli/commands/misc"
      autoload :Services,     "#{ROOT}/cli/commands/services"
      autoload :User,         "#{ROOT}/cli/commands/user"
      autoload :Manifest,     "#{ROOT}/cli/commands/manifest"
    end

這裏主要分成兩塊:一是cli目錄下的一些工具類,提供了Cli需要的一些通用功能,而另外一部分是在commands目錄下,代表了各種不同類型的命令,比如有關於app的(apps),或者有關於service的(services)。

3. Runner.run

前面提到bin/vmc執行命令就是調用Runner.run。那麼這個函數在vmc/lib/cli/runner.rb文件中。這個文件主要解析命令以及命令的參數,然後生成一個command,就是前面Cli.rb中提到的各種commands。解析命令其實比較簡單,就是分析是哪種類型的(app?service?misc?。。。),然後命令是什麼(list?target?。。。),最後參數是什麼。
在獲得這些數據之後就會嘗試生成一個command:

      cmd = VMC::Cli::Command.const_get(@namespace.to_s.capitalize)
      cmd.new(@options).send(@action, *@args.collect(&:dup))

這兩行代碼的意思是:首先在Command空間下找到namespace對應的常量string(namespace就是對應的類名,比如app的就是Apps,admin就是Admin)。獲得這個常量string是爲了new一個實例出來,cmd.new(@options)意思就是new一個這個cmd string命名的類。然後.send()是調用這個類的一個函數,函數名就是第一個參數@action,第二個參數就是這個函數的參數。可以看到ruby的反射真是非常的簡單。

舉個例子:比如我執行命令vmc stop appname。那麼這個命令會在runner.rb中解析爲:namespace:apps,action:stop,args:appname。然後new一個Apps實例,調用它的stop函數,把appname傳進去。

4. 實際執行

從上面的分析我們的流程已經到了command裏面了,那麼每個command會對命令作出相應的操作。那麼我們看Apps類好了,我們看一個list函數,這個函數對應了vmc apps命令(這裏list是apps的別名,所以我們可以不使用vmc apps list命令)。那麼看這個函數的代碼:

def list
      apps = client.apps
      apps.sort! {|a, b| a[:name] <=> b[:name] }
      return display JSON.pretty_generate(apps || []) if @options[:json]
 
      display "\n"
      return display "No Applications" if apps.nil? || apps.empty?
 
      apps_table = table do |t|
        t.headings = 'Application', '# ', 'Health', 'URLS', 'Services'
        apps.each do |app|
          t << [app[:name], app[:instances], health(app), app[:uris].join(', '), app[:services].join(', ')]
        end
      end
      display apps_table
    end

有一行代碼:apps = client.apps,我們暫時不管它,只需要知道它是獲得了當前cf中的所有app,賦值給apps。那麼後面就是在display了,可以返回json格式的,或者table格式的。在table中我們可以看到熟悉的apps table header。

那麼回到略過的那一行,我們需要知道client在哪裏:這個client其實就是vmc/lib/vmc/client.rb中Client類的實例,那麼我們開始看Client類:

5. Client.rb

Client負責一個任務:和CF的cloud controller交互。比如vmc apps,需要給cc發送一個獲得所有apps的請求。所以前面提到的client.apps就是調用它的apps函數,這個函數最終會通過request函數發出請求。

 
至此我們的流程已經全部走通了,VMC還是比較簡單的,希望大家能夠學到點東西

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章