rails是个很成熟的网站开发架构,设计者也与时俱进把很多先进的技术与架构集成到rails中,造就了其他框架无法比拟的开发效率。网站发展到一定程度,网站流量越来越大就不能把静态文件请求和动态网页请求放到同一台服务器。因为静态资源的流量会远远大于动态资源的请求,流量一大,静态资源会占满服务器带宽,导致网站加载缓慢,所以cdn是必不可少的。
个人原创,版权所有,转载请注明出处,并保留原文链接:
https://www.embbnux.com/2016/01/07/ruby_on_rails_assets_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/