分享

cloud controller 源码分析(包括Ruby on Rails项目结构分析)

问题导读:
1、cloud controller的代码结构是什么?
2、如何分析MVC结构?






本文记叙我作为一个完全不懂ror(ruby on rails)的开发者,了解ror以及cc所需的知识。全文包括了cloud controller的代码结构,MVC架构分析,最后有一个例子帮助分析。

目录结构
cc是一个ror项目,所以是按照ror的目录结构来安排的,如下所示。
11.JPG


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的,我们可以看一个例子:
  1. it 'should add the environment variable if its legal' do  
  2.   @args['env'] = ['foo=bar']  
  3.   headers_for(@user, nil, @args).each { |key, value| request.env[key] = value }  
  4.   post :create  
  5.   
  6.   get :get, :name => @app_name  
  7.   Yajl::Parser.parse(response.body)['env'].should == ['foo=bar']  
  8.   
  9. end  
复制代码


从第一行就可以看出,这块代码是check环境变量已经加载了,do代码块最后一行就是env环境变量should等于['foo=bar']。
vendor:存放第三方的代码库,具体还不清楚
config.ru:用来配置rack的文件,cc中使用了rack,所以需要这个文件。关于rack可以看这里。
我们可以从vcap/bin/cloud_controller文件里面找到cc的启动代码:
  1. if not CloudController.use_nginx  
  2.   server = Thin::Server.new(CloudController.bind_address, CloudController.external_port)  
  3. else  
  4.   socket = CloudController.instance_socket  
  5.   port =   CloudController.instance_port  
  6.   if socket and port  
  7.     $stderr.puts "only one of instance_socket or insecure_instance_port should be enabled in config file...quiting..."  
  8.     exit 1  
  9.   end  
  10.   if socket  
  11.     server = Thin::Server.new(socket)  
  12.   else  
  13.     $stderr.puts "Warning!!! starting up in a known insecure configuration."  
  14.     server = Thin::Server.new('127.0.0.1', port)  
  15.   end  
  16. end  
  17.   
  18.   
  19. cc_rack = File.join(cc_root, 'config.ru')  
  20. server.app = Rack::Adapter.load(cc_rack)  
  21. # The routers proxying to us handle killing inactive connections. Set an upper limit  
  22. # just to be safe.  
  23. server.timeout = 15 * 60 # 15 min  
  24. 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目录中,里面的内容类似:
  1. get    'apps'                      => 'apps#list',            :as => :list_apps  
  2. get    'apps/:name'                => 'apps#get',             :as => :app_get  
  3. put    'apps/:name'                => 'apps#update',          :as => :app_update  
  4. delete 'apps/:name'                => 'apps#delete',          :as => :app_delete  
  5. put    'apps/:name/application'    => 'apps#upload',          :as => :app_upload  
  6. get    'apps/:name/crashes'        => 'apps#crashes',         :as => :app_crashes  
  7. post   'resources'                 => 'resource_pool#match',  :as => :resource_match  
  8. 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方法:
  1. client.update_app(appname, app)  
复制代码


然后client会执行
  1. json_put(path(VMC::APPS_PATH, name), manifest)
复制代码

我还没有完全了解vmc的运行,这个instance的具体操作应该是藏在这些参数里面的,然后put完之后会发送到server端。


router过程
我们可以看到这个change instances发了一个update app的url,那么在routes.rb文件中找到
  1. put    'apps/:name'                => 'apps#update',          :as => :app_update
复制代码

那么再去找apps_controller的update方法。这个update方法调用了update_app_from_params方法,在update_app_from_params方法中有关instance解析的代码是:
  1. if changed.include?('instances')  
  2.   manager.change_running_instances(delta_instances)  
  3.   manager.updated  
  4.   
  5.   user_email = user ? user.email : 'N/A'  
  6.   CloudController.events.user_event(user_email, app.name, "Changing instances to #{app.instances}", :SUCCEEDED)  
  7.   
  8. end  
复制代码


manager就是models里面的app_manager。这个delta_instances就是需要改变的量,比如-1就是减少一个。
model实际操作
最终来到model,这个change_running_instances方法依据参数的正负来start或者stop instances。我们就看看start好了。start_instances主要就这句代码:
  1. dea_id = find_dea_for(message)  
  2.           json = Yajl::Encoder.encode(message)  
  3.           if dea_id  
  4.             CloudController.logger.debug("Sending start message #{json} to DEA #{dea_id}")  
  5.             NATS.publish("dea.#{dea_id}.start", json)  
复制代码


首先find一个dea,然后向NATS发布一个dea start 的消息,让dea start instance,然后就ok了

已有(2)人评论

跳转到指定楼层
ainubis 发表于 2015-3-29 20:59:42
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条