Rails rails s 启动过程

yhuiche · 2014年08月29日 · 最后由 zgm 回复于 2014年08月30日 · 9253 次阅读
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 文件后会执行文件代码,文件中classmodule的代码其实只是定义作用,所以本文中并不是所有的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',执行方法newstart


7) Thin::Server ,执行方法newstart
    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 回复信息

EventMachine 参考 http://wonderflow.info/wp-content/uploads/2013/07/EventMachine%E5%85%A5%E9%97%A8.pdf

好厉害的样子。

mark 下,空闲时间看看,内容太多。

Puby -> Ruby ~~

@jasl thank you。一不小心就错了。。╮(╯▽╰)╭

需要 登录 后方可回复, 如果你还没有账号请 注册新账号