【开源中国 APP 全新上线】“动弹” 回归、集成大模型对话、畅读技术报告”
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 回复信息