第一次发表话题,希望我新写的博客对社区有用
本文发表于 Embbnux 博客,欢迎转载,转载请注明原文出处,并保留原文链接:
https://www.embbnux.com/2016/01/07/ruby_on_rails_assets_cdn/
rails 是个很成熟的网站开发架构,设计者也与时俱进把很多先进的技术与架构集成到 rails 中,造就了其他框架无法比拟的开发效率。网站发展到一定程度,网站流量越来越大就不能把静态文件请求和动态网页请求放到同一台服务器。因为静态资源的流量会远远大于动态资源的请求,流量一大,静态资源会占满服务器带宽,导致网站加载缓慢,所以 cdn 是必不可少的。
rails 的开发者考虑得很全,要实现网站的 cdn 化,只需要修改一个配置文件即可,不过为了不显得我这篇博文太少了,我还是慢慢的讲来。
一个网页从在浏览器输入网址,到展示在你面前,之间经历了好多个请求,主要流程如下:
rails 对于的静态资源主要有两种,一种是写在代码里的,比如网站的 js、css 以及图标等文件,还有一种为用户上传的,比如用户上传的头像图片等。
对于静态文件等 rails 引入了Asset Pipeline用来管理编译静态资源,对于第一种写在代码里的静态文件,在 rails 工程里面是存在 app/assets 文件夹下,分别有 images 和 stylesheets 以及 javascripts 等分类文件夹,js 和 css 代码放在这里可以用 coffeescript 以及 sass 等各种语言来写,最终线上环境 (生产环境) 得对这些代码进行编译生成 js 以及 css 文件,这样浏览器才会识别,编译不仅进行格式的翻译还会进行 minify 等,最终会在编译文件后面加一段当前文件的 hash,所以代码变动,编译出的文件就不一样:
RAILS_ENV=production rake assets:precompile
#assets/javascripts/application.js => pubilic/application-3214abdc8899.js
由于编译完的文件被加了一段 hash 所以不能直接在 html 里面用路径访问,所以得在 view 层渲染时得出文件路径:
app/views/layout/application.html.erb:
<%= stylesheet_link_tag 'application' %>
<%= javascript_include_tag 'application' %>
<%= image_url 'favicon.png' %>
对应渲染出来的 html 是:
<link rel="stylesheet" media="screen" href="/assets/application-122fe15eeed76211bd37e2f1234454.css" />
<script src="/assets/application-583bffd2a21c2a6b8d1ab72bad4ba8af.js">
assets/favicon-1aa0e2adc41f64de39.png
加 hash 是为了在代码得到更新后浏览器能够及时更新使用新的静态文件,早期的 rails 版本使用的版本控制手段如下:
/stylesheets/application.css?1309495796
但是这种手段在 cdn 的使用场景上不能及时更新存在 cdn 上的文件,所以该用加了 hash 的文件名来做版本控制,保证 cdn 部署时代码和静态资源得到同时跟新,变成这样
application-1309495796.css
第二种静态资源是用户上传的,这些的不是放在代码里的,在 rails 工程中这些静态文件放在 public 文件夹下,因为 web 服务器的根目录指向的就是 public 文件夹,其他文件夹浏览器没有权限访问得到,之前的 js 和 css 也得被编译完后放到 public 文件夹才可以访问。这些静态文件不会被编译,所以文件名后面不会被加入 hash, 但也可以用 image_url‘upload/avatar.png’来访问,image_url 会自动区分要不要加 hash.
upload/avatar.png
说了这么多,是时候开启 cdn 了。如果一直是按 rails 规范来写的话在这里开启 cdn 配置,只需要在 config/environments/production.rb 加一句话:
config.action_controller.asset_host = 'static-cdn.embbnux.com'
这样之前渲染出来的 html 就变成这样:
<link rel="stylesheet" media="screen" href="http://static-cdn.embbnux.com/assets/application-122fe15eeed7688837e2f1234454.css" />
<script src="http://static-cdn.embbnux.com/assets/application-583bffd2a21c2a6b8d1a888ad4ba8af.js">
http://static-cdn.embbnux.comassets/favicon-1aa0e2adc41888de39.png
http://static-cdn.embbnux.com/upload/avatar.png
出来的路径就是指向 cdn 服务器上的静态资源了,这样一个网页的访问就只会有第一个 html 的请求发到我们服务器上,其他的静态资源请求是发到 cdn 服务器上的,一个 html 文本一半也就几 k,大小很小的,加载时间也很快,不很会占用服务器带宽。
有时候为了区分和管理第一种网站静态资源和第二种用户静态资源可以配置分别指向两个 cdn 域名,不同的域名用不同的 cdn 空间,可以这样配置
config.action_controller.asset_host = Proc.new { |source|
if source =~ /assets/
'static-assets-cdn.embbnux.com'
else
'static-images-cdn.embbnux.com'
end
}
这样渲染出来就变成这样:
<link rel="stylesheet" media="screen" href="http://static-assets-cdn.embbnux.com/assets/application-122fe15eeed76211bd37e2f1234454.css" />
<script src="http://static-assets-cdn.embbnux.com/assets/application-583bffd2a21c2a6b8d1882bad488af.js">
http://static-assets-cdn.embbnux.comassets/favicon-1aa0e2adc8884de39.png
http://static-images-cdn.embbnux.com/upload/avatar.png
为了保证 assets 文件及时更新到 cdn 存储,可以写个 rake 脚本利用 cdn 商提供的接口,在部署的时候及时上传 assets 文件到 cdn 空间比如:
rake cdn:assets_upload
为了保证 cdn 的文件能够及时与我们自己服务器上静态文件保持一致,需要开启 cdn 的镜像功能,但为了不使 cdn 缓存我们的动态 html 内容,造成镜像网站而降低网站权重,需要配置一下 nginx 服务器
server {
server_name www.embbnux.com;
root /rails_app/public;
location / {
index index.html index.htm;
proxy_pass http://rails_app_upstream;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
server_name static.embbnux.com;
root /rails_app/public;
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
location ~* ^/assets/ {
root /rails_app/public;
expires 1y;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
break;
}
}
这样服务器上的静态资源就可以通过 static.embbnux.com 访问,动态资源只能通过 www.embbnux.com 访问,cdn 镜像通过 static 域名,也就不用担心会镜像动态页面
开启 cdn 后也就不用担心静态资源来占我们服务器的带宽了,不过这也只是大型化网站的第一步,还可以进行很多优化,欢迎拍砖。流量在大一点,只接受动态资源请求的我们网站服务器也会承受不了的,这时候就是开启负载均衡的时候了,可以一个 nginx 代理请求,然后中转到几台服务器上的 rails_app_upstream,这都是后话了。
更多内容欢迎查看原文博客: https://www.embbnux.com/2016/01/07/ruby_on_rails_assets_cdn/