Ruby on rails 项目启动流程

众所周知,我们可以通过rails s 这个命令来启动一个rails 项目,但是这条命令都干了哪些事呢?抽时间研究了下,同时感谢tomwang1013的博客。当我们输入rails s 这个命令的时候,项目会加载项目bin/rails.rb 这个文件 
#!/usr/bin/env ruby 
APP_PATH = File.expand_path('../../config/application', __FILE__) 
require_relative '../config/boot' 
require 'rails/commands'
 
通过代码我们可以看到这个文件中定义了一个APP_PATH 即我们的项目文件:config/application.rb,并require了config/boot,boot文件主要是做了bundle的初始化。 
然后,我们可以看到这个时候rails/commands(railties-3.2.3/lib/rails/commands.rb)文件被load进来,由于我们传入的command参数为s,也就是server,然后我们看看commands的这个文件的源码对应的部分:

when 'server'                                                                                         
  # Change to the application's path if there is no config.ru file in current dir.                    
  # This allows us to run script/rails server from other directories, but still get                   
  # the main config.ru and properly set the tmp directory.                                            
  Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru"))  

  require 'rails/commands/server'                                                                     
  Rails::Server.new.tap { |server|                                                                    
    # We need to require application after the server sets environment,                               
    # otherwise the --environment option given to the server won't propagate.                         
    require APP_PATH                                                                                  
    Dir.chdir(Rails.application.root)                                                                 
    server.start                                                                                      
  }    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在server 这个方法中我们通过server.start这行代码可以看出最终调用了start这个方法,然后查看下start 这个方法。同时这里面也打印了我们熟悉的控制台信息

def start                                                                          
  url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}"  
  puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"                 
  puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}"  
  puts "=> Call with -d to detach" unless options[:daemonize]  
  trap(:INT) { exit }  
  puts "=> Ctrl-C to shutdown server" unless options[:daemonize]  
  #Create required tmp directories if not found                                    
  %w(cache pids sessions sockets).each do |dir_to_make|                            
    FileUtils.mkdir_p(Rails.root.join('tmp', dir_to_make))                         
  end                                                                              

  puts 'server start ---'  
  super  
ensure  
  # The '-h' option calls exit before @options is set.                             
  # If we call 'options' with it unset, we get double help banners.                
  puts 'Exiting' unless @options && options[:daemonize]                            
end   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这个start 这个方法最终会执行super 这个,super 会调用Rack::Server#start方法:代码

def start &blk  
  if options[:warn]  
    $-w = true                                                     
  end                                                              

  if includes = options[:include]  
    $LOAD_PATH.unshift(*includes)                                  
  end  

  if library = options[:require]                                   
    require library  
  end  

  if options[:debug]  
    $DEBUG = true                                                  
    require 'pp'  
    p options[:server]                                             
    pp wrapped_app  
    pp app  
  end  

  # Touch the wrapped app, so that the config.ru is loaded before  
  # daemonization (i.e. before chdir, etc).                        
  wrapped_app  

  daemonize_app if options[:daemonize]                             
  write_pid if options[:pid]  

  trap(:INT) do  
    if server.respond_to?(:shutdown)                               
      server.shutdown  
    else                                                           
      exit  
    end                                                            
  end  

  server.run wrapped_app, options, &blk  
end  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

代码执行到server.run wrapped_app 这行代码。wrapped_app方法最终又会调用Rack::Server#app

def app  
  @app ||= begin                                                                
    if !::File.exist? options[:config]                                          
      abort "configuration #{options[:config]} not found"                       
    end  

    app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)  
    self.options.merge! options  
    app  
  end  
end     
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

最终app 这个方法会加载项目config 目录下的environment.rb这个文件如:

# Load the Rails application.
require File.expand_path('../application', __FILE__)
require 'whenever'


module ProductConfig
  DYNAMIC_FIELDS = Hash.new
end

module RestConfig
  PRODUCT_SERVER = ENV["PHOTO_HOST"] || 'http://localhost:3001/'

  #CUSTOMER_SERVER = ENV["CUSTOMER_HOST"] || 'http://localhost:3001/'
  CUSTOMER_SERVER = ENV["CUSTOMER_HOST"] || 'http://localhost:3001/'

  OA_SERVER = 'http://localhost:3001/'

  #ELEPHANT_HOST = ENV["ELEPHANT_HOST"] || 'http://www.jiuyunda.net:90/'
  ELEPHANT_HOST = ENV["ELEPHANT_HOST"] || 'http://localhost:3001/'

  JXC_HOST = ENV["JXC_HOST"] || 'http://localhost:3001/'

  SETTLE_HOST = ENV["SETTLE_HOST"] || 'http://localhost:3001/'

end

# Initialize the Rails application.
Rails.application.initialize!
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

Rails.application.initialize!这行代码我们可以看出 
项目的初始化完成。这个时候我们代码就回到Rack::Server#start方法的最后一行 server.run wrapped_app, options, &blk 由于我们没有设定任何参数。通过代码

def server                                                                         
  @_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default(options)  
end      
def self.default(options = {})          
  # Guess.                              
  if ENV.include?("PHP_FCGI_CHILDREN")  
    # We already speak FastCGI          
    options.delete :File                
    options.delete :Port                

    Rack::Handler::FastCGI              
  elsif ENV.include?("REQUEST_METHOD")  
    Rack::Handler::CGI                  
  else                                  
    begin                               
      Rack::Handler::Thin               
    rescue LoadError                    
      Rack::Handler::WEBrick            
    end                                 
  end                                   
end            
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

我们可以看到rack 为我们选定了默认的server.一般来说是WEBrick .然后WEBrick启动,打印如下信息

INFO  WEBrick 1.3.1  
INFO  ruby 1.9.3 
INFO  WEBrick::HTTPServer#start: pid=28371 port=3000 
  • 1
  • 2
  • 3

如果安装了其他application server的话打印的信息可能会不同如

Booting Puma
=> Rails 4.2.4 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
"assets end:false"
Puma starting in single mode...
* Version 3.4.0 (ruby 2.3.0-p0), codename: Owl Bowl Brawl
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://localhost:3000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

至此项目的启动已完成。

posted @ 2018-05-06 17:48  lmg6406  阅读(2395)  评论(0编辑  收藏  举报