cloud controller 源碼分析(包括Ruby on Rails項目結構分析)

本文記敘我作爲一個完全不懂ror(ruby on rails)的開發者,瞭解ror以及cc所需的知識。全文包括了cloud controller的代碼結構,MVC架構分析,最後有一個例子幫助分析。

目錄結構

cc是一個ror項目,所以是按照ror的目錄結構來安排的,如下所示。


  • app:是web應用的主程序目錄,包含了mvc組件的代碼
  • config:runtime rules,routes,database等的配置
  • db:數據庫的schema以及 數據庫的migration(數據庫的migration就是表示數據庫schema更新的腳本,deploy時只需要執行這些腳本就可以做出相應的數據庫更新,是一種程序規範的功能)
  • lib:程序庫,供其他代碼調用
  • log:app的log目錄
  • public:就是訪問者可以直接訪問的目錄,有一些靜態文件
  • script:一些腳本,用來做些部署之類的事情
  • spec:RSpec的文件夾,RSpec是一個測試工具,他做的測試叫做Behaviour-Deiven測試。spec文件夾裏面的內容一般就是描述behaviour的,我們可以看一個例子:

    it 'should add the environment variable if its legal' do
      @args['env'] = ['foo=bar']
      headers_for(@user, nil, @args).each { |key, value| request.env[key] = value }
      post :create

      get :get, :name => @app_name
      Yajl::Parser.parse(response.body)['env'].should == ['foo=bar']

    end
從第一行就可以看出,這塊代碼是check環境變量已經加載了,do代碼塊最後一行就是env環境變量should等於['foo=bar']

  • vendor:存放第三方的代碼庫,具體還不清楚
  • config.ru:用來配置rack的文件,cc中使用了rack,所以需要這個文件。關於rack可以看這裏
我們可以從vcap/bin/cloud_controller文件裏面找到cc的啓動代碼:
if not CloudController.use_nginx
  server = Thin::Server.new(CloudController.bind_address, CloudController.external_port)
else
  socket = CloudController.instance_socket
  port =   CloudController.instance_port
  if socket and port
    $stderr.puts "only one of instance_socket or insecure_instance_port should be enabled in config file...quiting..."
    exit 1
  end
  if socket
    server = Thin::Server.new(socket)
  else
    $stderr.puts "Warning!!! starting up in a known insecure configuration."
    server = Thin::Server.new('127.0.0.1', port)
  end
end


cc_rack = File.join(cc_root, 'config.ru')
server.app = Rack::Adapter.load(cc_rack)
# The routers proxying to us handle killing inactive connections. Set an upper limit
# just to be safe.
server.timeout = 15 * 60 # 15 min
server.start
我們可以看到cc使用了Thin的web服務器,而同時load了一個rack。這個rack就是通過讀取config.ru來生成的。

CC的MVC

可以看到,CC幾乎沒有View,主要是Model和Controller,因爲確實沒有什麼頁面顯示需要cc來完成。在app夾裏面有四個目錄:

controllers和models就不用提了,helpers只有一個簡單的service helper,而subscriptions包含了向NATS訂閱消息的所有代碼。

controllers與routes.rb

在controllers中的類負責轉發請求至合適的model。在這些類中定義了許多的方法來轉發,而具體那個url會調用那個類的轉發就需要routes.rb文件了。routes.rb文件在config目錄中,裏面的內容類似:
  get    'apps'                      => 'apps#list',            :as => :list_apps
  get    'apps/:name'                => 'apps#get',             :as => :app_get
  put    'apps/:name'                => 'apps#update',          :as => :app_update
  delete 'apps/:name'                => 'apps#delete',          :as => :app_delete
  put    'apps/:name/application'    => 'apps#upload',          :as => :app_upload
  get    'apps/:name/crashes'        => 'apps#crashes',         :as => :app_crashes
  post   'resources'                 => 'resource_pool#match',  :as => :resource_match
  get    'apps/:name/application'    => 'apps#download',        :as => :app_download
每一行的第一項顯然是request類型,第二項是url格式,:name表示有一個參數,這個參數被命名爲name,箭頭後面的則是對應的controller及其方法名,比如apps#list就是調用apps的list方法。最後一個as就是規則的一個別名,暫時不清楚什麼作用。。
controllers中ApplicationCotroller是所有controller的基類,每個類都對應一類任務,比較容易理解。
models就不詳細說了,都是和具體事物相關的,接下來就舉一個例子好了

擴展instance實例解析

這裏解釋下vmc instances app_name num這個命令是如何讓instance產生變化的。

vmc發送命令

vmc接收到用戶指令會發送一個請求,在vmc/ib/cli/commands/apps.rb類的change_instances方法中調用了client的update_app方法:

client.update_app(appname, app)
然後client會執行

json_put(path(VMC::APPS_PATH, name), manifest)

我還沒有完全瞭解vmc的運行,這個instance的具體操作應該是藏在這些參數裏面的,然後put完之後會發送到server端。

router過程

我們可以看到這個change instances發了一個update app的url,那麼在routes.rb文件中找到
  put    'apps/:name'                => 'apps#update',          :as => :app_update

那麼再去找apps_controller的update方法。這個update方法調用了update_app_from_params方法,在update_app_from_params方法中有關instance解析的代碼是:

      if changed.include?('instances')
        manager.change_running_instances(delta_instances)
        manager.updated

        user_email = user ? user.email : 'N/A'
        CloudController.events.user_event(user_email, app.name, "Changing instances to #{app.instances}", :SUCCEEDED)

      end
manager就是models裏面的app_manager。這個delta_instances就是需要改變的量,比如-1就是減少一個。

model實際操作

最終來到model,這個change_running_instances方法依據參數的正負來start或者stop instances。我們就看看start好了。start_instances主要就這句代碼:
          dea_id = find_dea_for(message)
          json = Yajl::Encoder.encode(message)
          if dea_id
            CloudController.logger.debug("Sending start message #{json} to DEA #{dea_id}")
            NATS.publish("dea.#{dea_id}.start", json)
首先find一個dea,然後向NATS發佈一個dea start 的消息,讓dea start instance,然後就ok了






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