rails server 调研
访问流程
在浏览器发起请求后,URL 由路由(route.rb
)解析,确定交由哪个控制器控制并解析得到参数。之后调用 erb 模板,生成 HTML,将此 HTML 返回到浏览器进行渲染呈现。
以 cookbook3 项目作为示例,当我们访问 http://127.0.0.1:3000/
时,我们得到的日志如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Started GET "/" for 127.0.0.1 at 2023-10-24 19:24:03 +0800 ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC Processing by RecipesController#index as HTML Rendering layout layouts/application.html.erb Rendering recipes/index.html.erb within layouts/application Recipe Load (0.1ms) SELECT "recipes".* FROM "recipes" ↳ app/views/recipes/index.html.erb:6 Category Load (0.1ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] ↳ app/views/recipes/_recipe.html.erb:9 Rendered recipes/_recipe.html.erb (Duration: 86.3ms | Allocations: 11430) Category Load (0.0ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] ↳ app/views/recipes/_recipe.html.erb:9 Rendered recipes/_recipe.html.erb (Duration: 1.0ms | Allocations: 492) CACHE Category Load (0.0ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] ↳ app/views/recipes/_recipe.html.erb:9 Rendered recipes/_recipe.html.erb (Duration: 0.7ms | Allocations: 448) Rendered recipes/index.html.erb within layouts/application (Duration: 131.1ms | Allocations: 24227) Rendered layout layouts/application.html.erb (Duration: 362.1ms | Allocations: 50060) Completed 200 OK in 498ms (Views: 380.8ms | ActiveRecord: 0.9ms | Allocations: 67023)
|
在日志头记录了请求的类型、日期和来源。随后 ActiveRecord::SchemaMigration Load
进行数据库查询,检查数据库中的迁移版本。这是在检查数据库结构是否与应用程序期望的相匹配。
Processing by RecipesController#index as HTML
这句话意为该请求由 RecipeController
控制器进行处理,转发为 #index
操作,并且期待 HTML
响应。
Rendering layout layouts/application.html.erb
指出了正在呈现应用程序的主布局文件 application.html.erb
Rendering recipes/index.html.erb within layouts/application
意为 recipes/index.html.erb
视图嵌套于主布局中。
Recipe Load (0.1ms) SELECT "recipes".* FROM "recipes"
表示 SQL 查询,从数据库中查询 recipes 的数据。之后的几句日志都意为从数据库中查询、获取数据。
1 2 3
| Rendered recipes/_recipe.html.erb (Duration: 0.7ms | Allocations: 448) Rendered recipes/index.html.erb within layouts/application (Duration: 131.1ms | Allocations: 24227) Rendered layout layouts/application.html.erb (Duration: 362.1ms | Allocations: 50060)
|
表明已成功返回 Render,网页成功呈现。
Completed 200 OK in 498ms (Views: 380.8ms | ActiveRecord: 0.9ms | Allocations: 67023)
这是日志的结束部分,指明整个请求处理的耗费时间、状态码、以及资源分配信息。
Rails 启动流程
config/boot.rb
文件
此文件包含如下内容:
1 2 3 4
| ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
require "bundler/setup"
|
环境变量 BUNDLE_GEMFILE
的作用是制定 bundle 文件路径。
require "bundle/setup"
的目的是加载 Bundler 库并设置应用程序的环境。Bundler 是 Ruby 的依赖管理器,用于管理应用程序所需的 Gem 依赖项。
其具体会做以下事情:
- 加载 Bundle 库。
- 设置 Gem 加载路径。
- 检查 Gem 依赖项。
# require "bootsnap/setup" # Speed up boot time by caching expensive operations.
这句话会用于加速 rails 的启动速度。但是在我的本机上由于某些兼容问题,导致包含此选项会报错,无法启动 rails server,故而将其注掉。
bin/rails.rb
文件
内容如下:
1 2 3 4
| #!/usr/bin/env ruby.exe APP_PATH = File.expand_path("../config/application", __dir__) require_relative "../config/boot" require "rails/commands"
|
其中 rails/commands.rb
内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
require "rails/command"
aliases = { "g" => "generate", "d" => "destroy", "c" => "console", "s" => "server", "db" => "dbconsole", "r" => "runner", "t" => "test" }
command = ARGV.shift command = aliases[command] || command
Rails::Command.invoke command, ARGV
|
这部分内容主要在于对控制台指令的转发。例如,如果我们输入 rails s
,则会被解释成 rails server
。
Gem 包的加载顺序
列在 Gemfile
中的包通常是按照声明顺序加载的。有些 Gem 包之间可能存在依赖关系,Bundler 会确保存在依赖的 Gem 在加载时正确处理。