Rails的静态资源管理(四)—— 生产环境的 Asset Pipeline
官方文档:http://guides.ruby-china.org/asset_pipeline.html
http://guides.rubyonrails.org/asset_pipeline.html
在生产环境中,Sprockets 会使用前文介绍的指纹机制。默认情况下,Rails 假定静态资源文件都经过了预编译,并将由 Web 服务器处理。
在预编译阶段,Sprockets 会根据静态资源文件的内容生成 SHA256 哈希值,并在保存文件时把这个哈希值添加到文件名中。Rails 辅助方法会用这些包含指纹的文件名代替清单文件中的文件名。
例如,下面的代码:
<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "application" %>
会生成下面的 HTML:
<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js"></script>
<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen" rel="stylesheet" />
Rails 开始使用 Asset Pipeline 后,不再使用 :cache
和 :concat
选项,因此在调用 javascript_include_tag
和 stylesheet_link_tag
辅助方法时需要删除这些选项。
可以通过 config.assets.digest
初始化选项(默认为 true
)启用或禁用指纹功能。
在正常情况下,请不要修改默认的 config.assets.digest
选项(默认为 true
)。如果文件名中未包含指纹,并且 HTTP 头信息的过期时间设置为很久以后,远程客户端将无法在文件内容发生变化时重新获取文件。
1 预编译静态资源文件
Rails 提供了一个 Rake 任务,用于编译 Asset Pipeline 清单文件中的静态资源文件和其他相关文件。
经过编译的静态资源文件将储存在 config.assets.prefix
选项指定的路径中,默认为 /assets
文件夹。
部署 Rails 应用时可以在服务器上执行这个 Rake 任务,以便直接在服务器上完成静态资源文件的编译。关于本地编译的介绍,请参阅下一节。
这个 Rake 任务是:
$ RAILS_ENV=production bin/rails assets:precompile
Capistrano(v2.15.1 及更高版本)提供了对这个 Rake 任务的支持。只需把下面这行代码添加到 Capfile
中:
load 'deploy/assets'
就会把 config.assets.prefix
选项指定的文件夹链接到 shared/assets
文件夹。当然,如果 shared/assets
文件夹已经用于其他用途,我们就得自己编写部署任务了。
需要注意的是,shared/assets
文件夹会在多次部署之间共享,这样引用了这些静态资源文件的远程客户端的缓存页面在其生命周期中就能正常工作。
编译文件时的默认匹配器(matcher)包括 application.js
、application.css
,以及 app/assets
文件夹和 gem 中的所有非 JS/CSS 文件(会自动包含所有图像):
[ Proc.new { |filename, path| path =~ /app\/assets/ && !%w(.js .css).include?(File.extname(filename)) },
/application.(css|js)$/ ]
这个匹配器(及预编译数组的其他成员;见后文)会匹配编译后的文件名,这意味着无论是 JS/CSS 文件,还是能够编译为 JS/CSS 的文件,都将被排除在外。例如,.coffee
和 .scss
文件能够编译为 JS/CSS,因此被排除在默认的编译范围之外。
要想包含其他清单文件,或单独的 JavaScript 和 CSS 文件,可以把它们添加到 config/initializers/assets.rb
配置文件的 precompile
数组中:
Rails.application.config.assets.precompile += %w( admin.js admin.css )
添加到 precompile
数组的文件名应该以 .js
或 .css
结尾,即便实际添加的是 CoffeeScript 或 Sass 文件也是如此。
assets:precompile
这个 Rake 任务还会成生 .sprockets-manifest-md5hash.json
文件(其中 md5hash
是一个 MD5 哈希值),其内容是所有静态资源文件及其指纹的列表。有了这个文件,Rails 辅助方法不需要 Sprockets 就能获得静态资源文件对应的指纹。下面是一个典型的 .sprockets-manifest-md5hash.json
文件的例子:
{"files":{"application-aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b.js":{"logical_path":"application.js","mtime":"2016-12-23T20:12:03-05:00","size":412383,
"digest":"aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b","integrity":"sha256-ruS+cfEogDeueLmX3ziDMu39JGRxtTPc7aqPn+FWRCs="},
"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18.css":{"logical_path":"application.css","mtime":"2016-12-23T19:12:20-05:00","size":2994,
"digest":"86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18","integrity":"sha256-hqKStQcHk8N+LA5fOfc7s4dkTq6tp/lub8BAoCixbBg="},
"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda.ico":{"logical_path":"favicon.ico","mtime":"2016-12-23T20:11:00-05:00","size":8629,
"digest":"8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda","integrity":"sha256-jSOHuNTTLOzZP6OQDfDp/4nQGqzYT1DngMF8n2s9Dto="},
"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493.png":{"logical_path":"my_image.png","mtime":"2016-12-23T20:10:54-05:00","size":23414,
"digest":"f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493","integrity":"sha256-9AKBVv1+ygNYTV8vwEcN8eDbxzaequY4sv8DP5iOxJM="}},
"assets":{"application.js":"application-aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b.js",
"application.css":"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18.css",
"favicon.ico":"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda.ico",
"my_image.png":"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493.png"}}
.sprockets-manifest-md5hash.json
文件默认位于 config.assets.prefix
选项所指定的位置的根目录(默认为 /assets
文件夹)。
在生产环境中,如果有些预编译后的文件丢失了,Rails 就会抛出 Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError
异常,提示所丢失文件的文件名。
1.1 在 HTTP 首部中设置为很久以后才过期
预编译后的静态资源文件储存在文件系统中,并由 Web 服务器直接处理。默认情况下,这些文件的 HTTP 首部并不会在很久以后才过期,为了充分发挥指纹的作用,我们需要修改服务器配置中的请求头过期时间。
对于 Apache:
# 在启用 Apache 模块 `mod_expires` 的情况下,才能使用
# Expires* 系列指令。
<Location /assets/>
# 在使用 Last-Modified 的情况下,不推荐使用 ETag
Header unset ETag
FileETag None
# RFC 规定缓存时间为 1 年
ExpiresActive On
ExpiresDefault "access plus 1 year"
</Location>
对于 Nginx:
location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public; add_header ETag "";
}
2 本地预编译
在本地预编译静态资源文件的理由如下:
- 可能没有生产环境服务器文件系统的写入权限;
- 可能需要部署到多台服务器,不想重复编译;
- 部署可能很频繁,但静态资源文件很少变化。
本地编译允许我们把编译后的静态资源文件纳入源代码版本控制,并按常规方式部署。
有三个注意事项:
- 不要运行用于预编译静态资源文件的 Capistrano 部署任务;
- 开发环境中必须安装压缩或简化静态资源文件所需的工具;
- 必须修改下面这个设置:
在 config/environments/development.rb
配置文件中添加下面这行代码:
config.assets.prefix = "/dev-assets"
在开发环境中,通过修改 prefix
,可以让 Sprockets 使用不同的 URL 处理静态资源文件,并把所有请求都交给 Sprockets 处理。在生产环境中,prefix
仍然应该设置为 /assets
。在开发环境中,如果不修改 prefix
,应用就会优先读取 /assets
文件夹中预编译后的静态资源文件,这样对静态资源文件进行修改后,除非重新编译,否则看不到任何效果。
实际上,通过修改 prefix
,我们可以在本地预编译静态资源文件,并把这些文件储存在工作目录中,同时可以根据需要随时将其纳入源代码版本控制。开发模式将按我们的预期正常工作。
3 实时编译
在某些情况下,我们需要使用实时编译。在实时编译模式下,Asset Pipeline 中的所有静态资源文件都由 Sprockets 直接处理。
通过如下设置可以启用实时编译:
config.assets.compile = true
如前文所述,静态资源文件会在首次请求时被编译和缓存,辅助方法会把清单文件中的文件名转换为带 SHA256 哈希值的版本。
Sprockets 还会把 Cache-Control
HTTP 首部设置为 max-age=31536000
,意思是服务器和客户端浏览器的所有缓存的过期时间是 1 年。这样在本地浏览器缓存或中间缓存中找到所需静态资源文件的可能性会大大增加,从而减少从服务器上获取静态资源文件的请求次数。
但是实时编译模式会使用更多内存,性能也比默认设置更差,因此并不推荐使用。
如果部署应用的生产服务器没有预装 JavaScript 运行时,可以在 Gemfile 中添加一个:
group :production do
gem 'therubyracer'
end
4 CDN
CDN 的意思是内容分发网络,主要用于缓存全世界的静态资源文件。当 Web 浏览器请求静态资源文件时,CDN 会从地理位置最近的 CDN 服务器上发送缓存的文件副本。如果我们在生产环境中让 Rails 直接处理静态资源文件,那么在应用前端使用 CDN 将是最好的选择。
使用 CDN 的常见模式是把生产环境中的应用设置为“源”服务器,也就是说,当浏览器从 CDN 请求静态资源文件但缓存未命中时,CDN 将立即从“源”服务器中抓取该文件,并对其进行缓存。例如,假设我们在 example.com
上运行 Rails 应用,并在mycdnsubdomain.fictional-cdn.com
上配置了 CDN,在处理对 mycdnsubdomain.fictional-cdn.com/assets/smile.png
的首次请求时,CDN 会抓取 example.com/assets/smile.png
并进行缓存。之后再请求 mycdnsubdomain.fictional-cdn.com/assets/smile.png
时,CDN 会直接提供缓存中的文件副本。对于任何请求,只要 CDN 能够直接处理,就不会访问 Rails 服务器。由于 CDN 提供的静态资源文件由地理位置最近的 CDN 服务器提供,因此对请求的响应更快,同时 Rails 服务器不再需要花费大量时间处理静态资源文件,因此可以专注于更快地处理应用代码。
4.1 设置用于处理静态资源文件的 CDN
要设置 CDN,首先必须在公开的互联网 URL 地址上(例如 example.com
)以生产环境运行 Rails 应用。下一步,注册云服务提供商的 CDN 服务。然后配置 CDN 的“源”服务器,把它指向我们的网站 example.com
,具体配置方法请参考云服务提供商的文档。
CDN 提供商会为我们的应用提供一个自定义子域名,例如 mycdnsubdomain.fictional-cdn.com
(注意 fictional-cdn.com
只是撰写本文时杜撰的一个 CDN 提供商)。完成 CDN 服务器配置后,还需要告诉浏览器从 CDN 抓取静态资源文件,而不是直接从 Rails 服务器抓取。为此,需要在 Rails 配置中,用静态资源文件的主机代替相对路径。通过 config/environments/production.rb
配置文件的 config.action_controller.asset_host
选项,我们可以设置静态资源文件的主机:
config.action_controller.asset_host = 'mycdnsubdomain.fictional-cdn.com'
这里只需提供“主机”,即前文提到的子域名,而不需要指定 HTTP 协议,例如 http://
或 https://
。默认情况下,Rails 会使用网页请求的 HTTP 协议作为指向静态资源文件链接的协议。
还可以通过环境变量设置静态资源文件的主机,这样可以方便地在不同的运行环境中使用不同的静态资源文件:
config.action_controller.asset_host = ENV['CDN_HOST']
这里还需要把服务器上的 CDN_HOST
环境变量设置为 mycdnsubdomain.fictional-cdn.com
。
服务器和 CDN 配置好后,就可以像下面这样引用静态资源文件:
<%= asset_path('smile.png') %>
这时返回的不再是相对路径 /assets/smile.png
(出于可读性考虑省略了文件名中的指纹),而是指向 CDN 的完整路径:
http://mycdnsubdomain.fictional-cdn.com/assets/smile.png
如果 CDN 上有 smile.png
文件的副本,就会直接返回给浏览器,而 Rails 服务器甚至不知道有浏览器请求了 smile.png
文件。如果 CDN 上没有 smile.png
文件的副本,就会先从“源”服务器上抓取 example.com/assets/smile.png
文件,再返回给浏览器,同时保存文件的副本以备将来使用。
如果只想让 CDN 处理部分静态资源文件,可以在调用静态资源文件辅助方法时使用 :host
选项,以覆盖 config.action_controller.asset_host
选项中设置的值:
<%= asset_path 'image.png', host: 'mycdnsubdomain.fictional-cdn.com' %>
4.2 自定义 CDN 缓存行为
CDN 的作用是为内容提供缓存。如果 CDN 上有过期或不良内容,那么不仅不能对应用有所助益,反而会造成负面影响。本小节将介绍大多数 CDN 的一般缓存行为,而我们使用的 CDN 在特性上可能会略有不同。
4.2.1 CDN 请求缓存
我们常说 CDN 对于缓存静态资源文件非常有用,但实际上 CDN 缓存的是整个请求。其中既包括了静态资源文件的请求体,也包括了其首部。其中,Cache-Control
首部是最重要的,用于告知 CDN(和 Web 浏览器)如何缓存文件内容。假设用户请求了 /assets/i-dont-exist.png
这个并不存在的静态资源文件,并且 Rails 应用返回的是 404,那么只要设置了合法的 Cache-Control
首部,CDN 就会缓存 404 页面。
4.2.2 调试 CDN 首部
检查 CDN 是否正确缓存了首部的方法之一是使用 curl。我们可以分别从 Rails 服务器和 CDN 获取首部,然后确认二者是否相同:
$ curl -I http://www.example/assets/application-
d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK
Server: Cowboy
Date: Sun, 24 Aug 2014 20:27:50 GMT
Connection: keep-alive
Last-Modified: Thu, 08 May 2014 01:24:14 GMT
Content-Type: text/css
Cache-Control: public, max-age=2592000
Content-Length: 126560
Via: 1.1 vegur
CDN 中副本的首部:
$ curl -I http://mycdnsubdomain.fictional-cdn.com/application-
d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK Server: Cowboy Last-
Modified: Thu, 08 May 2014 01:24:14 GMT Content-Type: text/css
Cache-Control:
public, max-age=2592000
Via: 1.1 vegur
Content-Length: 126560
Accept-Ranges:
bytes
Date: Sun, 24 Aug 2014 20:28:45 GMT
Via: 1.1 varnish
Age: 885814
Connection: keep-alive
X-Served-By: cache-dfw1828-DFW
X-Cache: HIT
X-Cache-Hits:
68
X-Timer: S1408912125.211638212,VS0,VE0
在 CDN 文档中可以查询 CDN 提供的额外首部,例如 X-Cache
。
4.2.3 CDN 和 Cache-Control
首部
Cache-Control 首部是一个 W3C 规范,用于描述如何缓存请求。当未使用 CDN 时,浏览器会根据 Cache-Control
首部来缓存文件内容。在静态资源文件未修改的情况下,浏览器就不必重新下载 CSS 或 JavaScript 等文件了。通常,Rails 服务器需要告诉 CDN(和浏览器)这些静态资源文件是“公共的”,这样任何缓存都可以保存这些文件的副本。此外,通常还会通过 max-age
字段来设置缓存失效前储存对象的时间。max-age
字段的单位是秒,最大设置为 31536000,即一年。在 Rails 应用中设置 Cache-Control
首部的方法如下:
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=31536000'
}
现在,在生产环境中,Rails 应用的静态资源文件在 CDN 上会被缓存长达 1 年之久。由于大多数 CDN 会缓存首部,静态资源文件的 Cache-Control
首部会被传递给请求该静态资源文件的所有浏览器,这样浏览器就会长期缓存该静态资源文件,直到缓存过期后才会重新请求该文件。
4.2.4 CDN 和基于 URL 地址的缓存失效
大多数 CDN 会根据完整的 URL 地址来缓存静态资源文件的内容。因此,缓存
http://mycdnsubdomain.fictional-cdn.com/assets/smile-123.png
和缓存
http://mycdnsubdomain.fictional-cdn.com/assets/smile.png
被认为是两个完全不同的静态资源文件的缓存。
如果我们把 Cache-Control
HTTP 首部的 max-age
值设得很大,那么当静态资源文件的内容发生变化时,应同时使原有缓存失效。例如,当我们把黄色笑脸图像更换为蓝色笑脸图像时,我们希望网站的所有访客看到的都是新的蓝色笑脸图像。如果我们使用了 CDN,并使用了 Rails Asset Pipeline config.assets.digest
选项的默认值 true
,一旦静态资源文件的内容发生变化,其文件名就会发生变化。这样,我们就不需要每次手动使某个静态资源文件的缓存失效。通过使用唯一的新文件名,我们就能确保用户访问的总是静态资源文件的最新版本。
Rails的静态资源管理(四)—— 生产环境的 Asset Pipeline的更多相关文章
- Rails的静态资源管理(五)—— 自定义 Asset Pipeline
官方文档:http://guides.ruby-china.org/asset_pipeline.html http://guides.rubyonrails.org/asset_pipeline.h ...
- Rails的静态资源管理(二)—— 如何使用 Asset Pipeline
官方文档:http://guides.ruby-china.org/asset_pipeline.html http://guides.rubyonrails.org/asset_pipeline.h ...
- Rails的静态资源管理(三)—— 开发环境的Asset Pipelin
官方文档:http://guides.ruby-china.org/asset_pipeline.html http://guides.rubyonrails.org/asset_pipeline.h ...
- Rails的静态资源管理(六)—— Asset Pipeline缓存存储方式、预处理、升级等
官方文档:http://guides.ruby-china.org/asset_pipeline.html http://guides.rubyonrails.org/asset_pipeline.h ...
- Rails的静态资源管理(一)——Asset Pipeline是什么
官方文档:http://guides.ruby-china.org/asset_pipeline.html http://guides.rubyonrails.org/asset_pipeline.h ...
- [django]Django的css、image和js静态文件生产环境配置
前言:在Django中HTML文件如果采用外联的方式引入css,js文件或者image图片,一般采用<link rel="stylesheet" href="../ ...
- Ruby Rails学习中:Sass 和 Asset Pipeline,布局中的链接(Rails路由,具名路由),用户注册: 第一步
接上篇: 一.Sass 和 Asset Pipeline Rails 中最有用的功能之一是 Asset Pipeline, 它极大地简化了静态资源文件(CSS.JavaScript 和图像)的生成和管 ...
- 使用Asset Pipeline管理rails生产环境静态资源实现步骤
1. 修改项目中指向静态资源文件的链接 a) 访问静态资源文件 <%= stylesheet_link_tag "application", media: &q ...
- 第四百零二节,Django+Xadmin打造上线标准的在线教育平台—生产环境部署,uwsgi安装和启动,nginx的安装与启动,uwsgi与nginx的配置文件+虚拟主机配置
第四百零二节,Django+Xadmin打造上线标准的在线教育平台—生产环境部署,uwsgi安装和启动,nginx的安装与启动,uwsgi与nginx的配置文件+虚拟主机配置 软件版本 uwsgi- ...
随机推荐
- bat定时检测系统服务是否开启
@echo offrem 定义循环间隔时间和监测的服务:set secs=90set srvname="Apache2a" echo.echo ================== ...
- C#中的转义字符verbatim string
In a verbatim string (a string starting with @"") to escape double quotes you use double q ...
- nohup后台运行jar与关闭
nohup 用途:LINUX命令用法,不挂断地运行命令. 语法:nohup Command [ Arg ... ] [ & ] 描述:nohup 命令运行由 Command 参数和任何相关 ...
- JavaWeb -- Servlet Filter 过滤器
1. Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter.通过Filter技术,开发人员可以实现用户在 ...
- python之Django rest_framework总结
一.rest api a.api就是接口 如: - http://www.oldboyedu.com/get_user/ - http://www. ...
- R语言可视化
R语言基础(一) 可视化基础 ##数据获取 x1=round(runif(100,min=80,max=100)) x2=round(rnorm(100,mean=80, sd=7)) x3=ro ...
- spring boot: @Retention注解 @Documented 注解 @Inherited 注解
http://www.jb51.net/article/55371.htm Retention注解 Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值:1.Retenti ...
- 如何破解mssql for linux 3.5G内存的限制
在上有篇博客中主要介绍了如何在CentOS 中安装和配置mssql ,在安装过程中遇到3.5G内存的限制,下面介绍如何去破解, 微软发布了SQLServer for Linux,但是安装竟然需要3.5 ...
- WPF各种控件详解——(WPF从我炫系列)
http://blog.csdn.net/zx13525079024/article/details/5694638
- ES6-Set和Map数据结构学习笔记
Set和Map数据结构 Set 基本用法 ES6提供了新的数据结构--Set,类似于数组,但是成员的值是唯一的,没有重复的值,Set本身是一种构造函数,用来生成Set数据结构 var s = new ...