rails s 启动过程

路边 发布于 2014/08/29 21:46
阅读 1K+
收藏 1

1) 命令 rails s 

windows) 这里会执行 RUBY_PATH/bin/rails.bat 文件
之后将执行权交给 'RUBY_PATH/bin/rails'

linux) 这里会执行 'RUBY_PATH/bin/rails'

跳转) 在 RUBY_PATH/bin/rails 中
load Gem.bin_path('railties', 'rails', version)




-->>这里将跳转的railties(gem)目录中 'RAILTIES_PATH/bin/rails'


2) railties(gem)

bin/rails)
在 RAILTIES_PATH/bin/rails 有
require "rails/cli"




(require 文件后会执行文件代码,文件中class、module的代码其实只是定义作用,所以本文中并不是所有的require语句都会提示跳转)
-->>这里将跳转交到 "RAILTIES_PATH/lib/rails/cli.rb"

lib/cli)
在 RAILTIES_PATH/lib/rails/cli.rb 中有
Rails::ScriptRailsLoader.exec_script_rails!




-->>这里将跳转交到 'RAILTIES_PATH/lib/script_rails_loader.rb' , 方法 'exec_script_rails!'

lib/script_rails_loader)
在 RAILTIES_PATH/lib/script_rails_loader.rb 中
调用方法   
exec_script_rails!



 
其中的
exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application?




# 这里会跳转到rails项目(需要运行的rails项目) APP_ROOT_PATH/script/rails
# exec 将以命令行的方式运行其接受的参数,并跳出当前脚本
# RUBY 及 RUBY_PATH/bin/ruby
# SCRIPT_RAILS 为 script/rails (注:'rails s'在APP所在目录执行)
-->>这里会跳转到rails项目 'APP_ROOT_PATH/script/rails'


3) APP

在 'APP_ROOT_PATH/script/rails' 中有
APP_PATH = File.expand_path('../../config/application',  __FILE__)
require File.expand_path('../../config/boot',  __FILE__)
require 'rails/commands'




# 设置了 APP_PATH
# 加载 bundle 
# 跳转到  "RAILTIES_PATH/lib/rails/commands.rb"
-->>跳转到 "RAILTIES_PATH/lib/rails/commands.rb"




4) RAILTIES_PATH/lib/rails/commands

在 "RAILTIES_PATH/lib/rails/commands.rb" 中
when 'server'
 Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru"))

 require 'rails/commands/server'
 Rails::Server.new.tap { |server|
   require APP_PATH
   Dir.chdir(Rails.application.root)
   server.start
 }




# Rails::Server 继承与 Rack::Server,这里(server.start)跳转到了rack
def tap
   yield self
   self
 end




-->>跳转到 'RAILTIES_PATH/lib/rails/commands/server.rb' Rails::Server (rack的一个子类),执行方法 new, start


5) Rails::Server 执行方法 new, start

这里主要加载rack middleware(中间件),加载 APP_PATH/config.ru 文件,将APP::Application(要启动的rails项目的 Application类)加载到rack
在 'RAILTIES_PATH/lib/rails/commands/server.rb' 中
def start
server.run wrapped_app, options, &blk




# Rails::Server 继承与 Rack::Server
# a) wrapped_app 加载了APP::Application以及中间件
# b) 这里 server 其实就是Webrick或者Thin等服务


a) wrapped_app 加载了APP::Application以及中间件
def wrapped_app
 @wrapped_app ||= build_app app
end

def build_app(app)
 middleware[options[:environment]].reverse_each do |middleware|
   middleware = middleware.call(self) if middleware.respond_to?(:call)
   next unless middleware
   klass = middleware.shift
   app = klass.new(app, *middleware)
 end
 app
end




# 这里主要是中间件的加载过程。rack中,中间件的定义例如
class Runtime
     def initialize(app, name = nil)
     @app = app
     ....
     end
     def call(env)
 status, headers, body = @app.call(env)
 ...
   end
     ...
 end




# 这里的 app 参数传进去的其实就是一个中间件或者 APP::Application。即中间件形成了一个嵌套的结构,这个结构在展开(调用call方法)的时候是跟方便的,

def app
app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)
...
end




# 这里options[:config]指向config.ru文件
# 在 Rack::Builder.parse_file 中有
# app = eval "Rack::Builder.new {\n" + cfgfile + "\n}.to_app",TOPLEVEL_BINDING, config, 0
# cfgfile 为 config.ru文件 的具体类容
# 在 Rack::Builder 中有
def initialize(default_app = nil,&block)
   @use, @map, @run = [], nil, default_app
   instance_eval(&block) if block_given?
 end
 def run(app)
   @run = app
 end




# 在config.ru中有
run APP::Application




# 这里就将APP::Application加载都了rack中


b) 这里 server 其实就是Webrick或者Thin等服务
在Rack::Server中有
def server
 @_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
end




# 跟踪在Rack::Handler中有
# def default
 begin
   Rack::Handler::Thin
 rescue LoadError
   Rack::Handler::WEBrick
 end




# rack 中缺省的服务选择的事Thin,但在rails中没有加载thin的gem,所以会使用webrick。如果要使用thin,在gemfile中添加thin的gem即可
-->> 这里会跳转到 Rack::Handler::Thin 'RACK_PATH/lib/rack/handler/thin.rb'(本文使用thin),执行方法run,



6) Rack::Handler::Thin 执行方法run

在Rack::Handler::Thin中有
def self.run(app, options={})
 server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
                             options[:Port] || 8080,
                             app)
 yield server if block_given?
 server.start
end




# 可以看到这里调用了::Thin::Server的方法new、start
# 这里 app 参数包含了APP::Application,以及中间件
-->> 这里会跳转到 Thin::Server 'THIN_PATH/lib/thin/server.rb',执行方法new、start




7) Thin::Server ,执行方法new、start

在Thin::Server中有
# def initialize(*args, &block)
args.each do |arg|
 case arg
 when Fixnum, /^\d+$/ then port    = arg.to_i
 when String          then host    = arg
 when Hash            then options = arg
 else
   @app = arg if arg.respond_to?(:call)
 end
end
@backend = select_backend(host, port, options)
@backend.server = self


# def start
@backend.start { setup_signals if @setup_signals }




# 可以看做 @app 是上面(6)中传过来的参数,包含了APP::Application,以及中间件
# 这里将 Thin::Server的实例绑到了@backend中,及将app绑定到了@backend中
# 跟踪 select_backend 有
# Backends::TcpServer.new(host, port) # rails 启动时select_backend会返回该值
-->>这里会跳转到 Backends::TcpServer 'THIN_PATH/lib/thin/backends/tcp_server.rb',执行start




8) Backends::TcpServer ,执行start

Backends::TcpServer 继承了 Base(Thin::Backends::Base)
在 Thin::Backends::Base 中有
def start
 @stopping = false
 starter   = proc do
   connect
   yield if block_given?
   @running = true
 end
 
 # Allow for early run up of eventmachine.
 if EventMachine.reactor_running?
   starter.call
 else
   @started_reactor = true
   EventMachine.run(&starter)
 end
end
def initialize_connection(connection)
 connection.backend                 = self
 connection.app                     = @server.app
 connection.comm_inactivity_timeout = @timeout
 connection.threaded                = @threaded
 ...
end




# 这里调用了connect方法,该方法在Backends::TcpServer中有定义
# def connect
#   @signature = EventMachine.start_server( @host , @port, Connection, &method(:initialize_connection))
# end
# 这里Connection是 Thin::Connection,'THIN_PATH/lib/thin/connection.rb'
# 这里生成了一个Connection实例,绑定了上面传过来的app。并将Connection实例传给了EventMachine
# 这里可以看出,thin使用了 EventMachine,EventMachine是一个基于Reactor设计模式的、用于网络编程和并发编程的事件驱动框架。
# 最后的启动交给了EventMachine,由EventMachine完成启动。
# EventMachine比较底层了,可以对消息进行接收与回复,下面是一个例子




9) EventMachine 例子

require 'eventmachine'


 	Str = <<Eos
<!DOCTYPE html>
<html>
  <head>
      <title>hello, eventmachine </title>
  </head>
  <body>
    <h1 style='color:#ff0000'>hello, eventmachine </h1>
  </body>
</html>
Eos
class Pass < EventMachine::Connection
 def receive_data data
   p data
   send_data Str
   close_connection_after_writing
 end
end
def test hh
 p 111111111111111
 p hh
end


EM.run do
 EM.start_server('0.0.0.0',10001,Pass,&method(:test))
end




# 这个例子可以通过浏览器访问 http://127.0.0.1:10001
# 每次收到消息后,都会执行 receive_data
# 由 send_data 回复信息
加载中
0
路边
路边
EventMachine 参考 http://wonderflow.info/wp-content/uploads/2013/07/EventMachine%E5%85%A5%E9%97%A8.pdf
返回顶部
顶部