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 回复信息
EventMachine 参考 http://wonderflow.info/wp-content/uploads/2013/07/EventMachine%E5%85%A5%E9%97%A8.pdf