日本免费精品_最新日韩一区_亚洲视频一区在线_a在线视频观看_天天射夜夜骑_粉嫩av一区二区三区_欧美中日韩免费视频_综合图区欧美_国内精品美女在线观看_午夜精品久久久久久久男人的天堂

首頁 > 編程 > Ruby > 正文

web 應用中常用的各種 cache詳解

2020-10-29 19:44:58
字體:
來源:轉載
供稿:網友

本文以Nginx,Rails,Mysql,Redis作為例子,換成其他web服務器,語言,數據庫,緩存服務都是類似的。
以下是3層的示意圖,方便后續引用:

1. 客戶端緩存

一個客戶端經常會訪問同一個資源,比如用瀏覽器訪問網站首頁或查看同一篇文章,或用app訪問同一個api,如果該資源和他之前訪問過的沒有任何改變,就可以利用http規范中的304 Not Modified 響應頭(http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5),直接用客戶端的緩存,而無需在服務器端再生成一次內容。
在Rails里面內置了fresh_when這個方法,一行代碼就可以完成:

class ArticlesController def show  @article = Article.find(params[:id])  fresh_when :last_modified => @article.updated_at.utc, :etag => @article endend

下次用戶再訪問的時候,會對比request header里面的If-Modified-Since和If-None-Match,如果相符合,就直接返回304,而不再生成response body。

但是這樣會遇到一個問題,假設我們的網站導航有用戶信息,一個用戶在未登陸專題訪問了一下,然后登陸以后再訪問,會發現頁面上顯示的還是未登陸狀態。或者在app訪問一篇文章,做了一下收藏,下次再進入這篇文章,還是顯示未收藏狀態。解決這個問題的方法很簡單,將用戶相關的變量也加入到etag的計算里面:

  fresh_when :etag => [@article.cache_key, current_user.id]  fresh_when :etag => [@article.cache_key, current_user_favorited]

另外提一個坑,如果nginx開啟了gzip,對rails執行的結果進行壓縮,會將rails輸出的etag header干掉,nginx的開發人員說根據rfc規范,對proxy_pass方式處理必須這樣(因為內容改變了),但是我個人認為沒這個必要,于是用了粗暴的方法,直接將src/http/modules/ngx_http_gzip_filter_module.c這個文件里面的這行代碼注釋掉,然后重新編譯nginx:

  //ngx_http_clear_etag(r); 

或者你可以選擇不改變nginx源代碼,將gzip off掉,將壓縮用Rack中間件來處理:

  config.middleware.use Rack::Deflater

除了在controller里面指定fresh_when以外,rails框架默認使用Rack::ETag middleware,它會自動給無etag的response加上etag,但是和fresh_when相比,自動etag能夠節省的只是客戶端時間,服務器端還是一樣會執行所有的代碼,用curl來對比一下。
Rack::ETag自動加入etag:

curl -v http://localhost:3000/articles/1< Etag: "bf328447bcb2b8706193a50962035619"< X-Runtime: 0.286958curl -v http://localhost:3000/articles/1 --header 'If-None-Match: "bf328447bcb2b8706193a50962035619"'< X-Runtime: 0.293798用fresh_when: curl -v http://localhost:3000/articles/1 --header 'If-None-Match: "bf328447bcb2b8706193a50962035619"'< X-Runtime: 0.033884

2. Nginx緩存

有一些資源可能會被調用很多,又無關用戶狀態,并且很少改變,比如新聞app上的列表api,購物網站上ajax請求分類菜單,可以考慮用Nginx來做緩存。
主要有2種實現方法:
A. 動態請求靜態文件化
在rails請求完成以后,將結果保存成靜態文件,后續請求就會直接由nginx提供靜態文件內容,用after_filter來實現一下:

class CategoriesController < ActionController::Base after_filter :generate_static_file, :only => [:index] def index  @categories = Category.all end def generate_static_file  File.open(Rails.root.join('public', 'categories'), 'w') do |f|   f.write response.body  end endend

另外我們需要在任何分類更新的時候,刪除掉這個文件,避免緩存不刷新的問題:

class Category < ActiveRecord::Base after_save :delete_static_file after_destroy :delete_static_file def delete_static_file  File.delete Rails.root.join('public', 'categories') endend

Rails 4之前,處理這種生成靜態文件緩存可以用內置的caches_page, rails 4之后變成了一個獨立gem actionpack-page_caching,和手工代碼對比一下,

class CategoriesController < ActionController::Base caches_page :index def update  #...  expire_page action: 'index' endend

如果只有一臺服務器,這個方法簡單又實用,但是如果有多臺服務器,就會出現更新分類只能刷新自己本身這臺服務器緩存的問題,可以用nfs來共享靜態資源目錄解決,或者用第2種:

B. 靜態化到集中緩存服務
首先我們得讓Nginx有直接訪問緩存的能力:

 upstream redis {  server redis_server_ip:6379; } upstream ruby_backend {  server unicorn_server_ip1 fail_timeout=0;  server unicorn_server_ip2 fail_timeout=0; } location /categories {  set $redis_key $uri;  default_type  text/html;  redis_pass redis;  error_page 404 = @httpapp; } location @httpapp {  proxy_pass http://ruby_backend; }

Nginx首先會用請求的uri作為key去redis里面獲取,如果獲取不到(404)就轉發給unicorn進行處理,然后改寫generate_static_file和delete_static_file方法:

 redis_cache.set('categories', response.body) redis_cache.del('categories')

這樣除了集中管理以外,還能夠設置緩存的失效時間,對于一些更新無時效性要求的數據,就可以不用處理刷新機制,簡單地固定時間刷新一次:

 redis_cache.setex('categories', 3.hours.to_i, response.body)

3. 整頁緩存

Nginx緩存在處理帶參數資源或者有用戶狀態的請求時候,就非常難以處理,這個時候可以用到整頁緩存。
比如說分頁請求列表,我們可以將page參數加入到cache_path:

class CategoriesController caches_action :index, :expires_in => 1.day, :cache_path => proc {"categories/index/#{params[:page].to_i}"}end

比如說我們只需要針對rss輸出進行緩存8小時:

class ArticlesController caches_action :index, :expires_in => 8.hours, :if => proc {request.format.rss?}end

再比如說對于非登陸用戶,我們可以緩存首頁:

class HomeController caches_action :index, :expires_in => 3.hours, :if => proc {!user_signed_in?}end

4. 片段緩存

如果說前面2種緩存能夠用到的場景有限,那么片段緩存是適用性最廣的。

場景1:我們需要在每個頁面一段廣告代碼,用來顯示不同廣告,如果沒有使用片段緩存,那么每個頁面都會要去查詢廣告的代碼,并且花費一定時間去生成html代碼:

- if advert = Advert.where(:name => request.controller_name + request.action_name, :enable => true).first div.ad  = advert.content

加了片段緩存以后,就可以少去這個查詢:

- cache "adverts/#{request.controller_name}/#{request.action_name}", :expires_in => 1.day do - if advert = Advert.where(:name => request.controller_name + request.action_name, :enable => true).first  div.ad   = advert.content

場景2:閱讀文章,文章的內容可能比較長時間都不會改變,經常變化可能是文章評論,就可以對文章主體部分加上片段緩存:

- cache "articles/#{@article.id}/#{@article.updated_at.to_i}" do div.article  = @article.content.markdown2html

節約了生成markdown語法轉換到html時間,這里用文章最后更新時間作為cache key的一部分,文章內容如果有改變,緩存自動失效,默認activerecord的cache_key方法也是用updated_at,你也可以加入更多的參數,比如article上有評論數的counter cache,更新評論數的時候不會更新文章時間,可以將這個counter也加入到key的一部分

場景3:復雜頁面結構的生成
數據結構比較復雜的頁面,在生成的時候避免不了大量的查詢和html渲染,用片段緩存,可以將這部分時間大大地節約,以我們網站游記頁面http://chanyouji.com/trips/109123(請允許小小地打個廣告,帶點流量)來說:
需要獲取天氣數據,照片數據,文本數據等,同時還要生成meta,keyword等seo數據,而這些內容又是和其他動態內容交叉,片段緩存就可以分開多個:

- cache "trips/show/seo/#{@trip.fragment_cache_key}", :expires_in => 1.day do title #{trip_name @trip} meta name="description" content="..." meta name="keywords" content="..."body div  ...- cache "trips/show/viewer/#{@trip.fragment_cache_key}", :expires_in => 1.day do - @trip.eager_load_all

小貼士,我在trip對象里面加了一個eager_load_all方法,緩存沒有命中的時候,查詢的時候避免出現n+1問題:

 def eager_load_all  ActiveRecord::Associations::Preloader.new([self], {:trip_days => [:weather_station_data, :nodes => [:entry, :notes => [:photo, :video, :audio]]]}).run end

小技巧1:帶條件的片段緩存
和caches_action不同,rails自帶的片段緩存是不支持條件的,比如說我們想未登陸用戶給他用片段緩存,而登陸用戶不使用,寫起來就很麻煩,我們可以改寫一下helper就可以了:

 def cache_if (condition, name = {}, cache_options = {}, &block)  if condition   cache(name, cache_options, &block)  else   yield  end end- cache_if !user_signed_in?, "xxx", :expires_in => 1.day do

小技巧2:關聯對象的自動更新
常使用對象update_at時間戳來作為cache key,可以在關聯對象上加上touch選項,自動更新關聯對象時間戳,比如我們可以在更新或者刪除文章評論的時候,自動個更新:

class Article has_many :commentsendclass Comment belongs_to :article, :touch => trueend

5. 數據查詢緩存

通常來說web應用性能瓶頸都出現在DB IO上,做好數據查詢緩存,減少數據庫的查詢次數,可以極大提高整體響應時間。
數據查詢緩存分2種:
A. 同一個請求周期內的緩存
舉一個顯示文章列表的例子,輸出文章標題和文章類別,對應代碼如下

# controller def index  @articles = Article.first(10) end# view- @articles.each do |article| h1 = article.name span = article.category.name

會發生10條類似的sql查詢:

SELECT `categories`.* FROM `categories` WHERE `categories`.`id` = ?

rails內置了query cache(https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb),在同一個請求周期內,如果沒有update/delete/insert的操作,會對相同的sql查詢進行緩存,如果文章類別都是相同的話,真正去查詢數據庫只會有1次。

如果文章類別都不一樣,就會出現N+1查詢問題(常見的性能瓶頸),rails推薦的解決方法是用Eager Loading Associations (http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations)

 

 def index  @articles = Article.includes(:category).first(10) end

查詢語句會變成

SELECT `categories`.* FROM `categories` WHERE `categories`.`id` in (?,?,?...)

B. 跨請求周期的緩存

同請求周期緩存所帶來性能優化是很有限的,很多時候我們需要用跨請求周期的緩存,將一些常用的數據(比如User model)緩存,對于active record來說,利用統一的查詢接口來fetch cache,利用callback來expire cache,就很容易實現,而且有一些現成的gem可以來用。

比如說 identity_cache (https://github.com/Shopify/identity_cache)

class User < ActiveRecord::Base include IdentityCacheendclass Article < ActiveRecord::Base include IdentityCache cached_belongs_to :userend# 都會命中緩存User.fetch(1)Article.find(2).user

這個gem的優點是代碼實現簡單,cache設置靈活,也方便擴展,缺點是需要用不同的查詢方法名(fetch),以及額外的關系定義。

如果想在無數據緩存的應用無縫加入緩存功能,推薦@hooopo做的second_level_cache (https://github.com/hooopo/second_level_cache) 。

class User < ActiveRecord::Base acts_as_cached(:version => 1, :expires_in => 1.week)end

#還是使用find方法,就會命中緩存

User.find(1)
#無需額外用不一樣的belongs_to定義
Article.find(2).user
實現原理是擴展了active record底層arel sql ast處理 (https://github.com/hooopo/second_level_cache/blob/master/lib/second_level_cache/arel/wheres.rb)
它的優點是無縫接入,缺點是擴展比較困難,對于只獲取少量字段的查詢無法緩存。

6. 數據庫緩存

編輯中

這6種緩存,分布在客戶端到服務器端不同的位置,所能夠節約的時間也正好從多到少依次排列。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
在线一区免费| 日韩欧美一级视频| 亚洲三级免费看| 一级网站免费观看| 久草视频在线看| 国产欧美高清在线| 日韩精品在线观看视频| 成人xxxx| 中文字幕在线国产| 91精品视频免费在线观看| 一区二区三区免费看视频| 欧美日韩高清| 日本黄色一区二区| 视频一区三区| 久久手机免费观看| 国产一级片网站| 日韩视频一区| av免费在线网站| 久久香蕉精品| 婷婷中文字幕一区三区| 精品激情国产视频| 91吃瓜在线观看| 一区二区91| 欧美日韩免费视频| 日韩在线小视频| 日本黄色一区二区三区| 97天天综合网| 欧美日韩国产综合视频在线观看中文| 午夜av一区二区| 久久视频免费看| 亚洲永久字幕| www.av中文字幕| 人人做人人澡人人爽欧美| 中文不卡在线| 国产在线拍揄自揄拍| 日韩美女视频一区二区在线观看| 亚洲中文字幕在线一区| 欧美日韩一级黄| 免费网站看黄yyy222| 中文字幕亚洲欧美| 日韩 欧美 亚洲| 日韩欧美国产综合| 日韩一级视频在线观看| 欧美日韩高清一区二区| 国产乱码午夜在线视频| 欧美日韩精品在线播放| 最近高清中文在线字幕在线观看| 久久精品日韩欧美| 国产羞羞视频在线播放| 日韩中文字幕在线视频播放| 99亚洲视频| 一区二区视频在线观看免费的| 亚洲黄色片在线观看| 1区2区3区在线视频| 久久香蕉av| 91日韩中文字幕| 中文在线一区二区| 成人久久久久| av三级在线播放| 久久精品蜜桃| 亚洲乱码在线观看| 国产一级免费在线观看| 日韩中文字幕亚洲| 欧美日韩精品不卡| 久久精品国产2020观看福利| 日韩精品视频在线| 欧美日韩综合视频网址| 亚洲二区自拍| 91精品国产综合久久福利| 亚洲第一福利视频在线| 五月天久久比比资源色| 91精品国产色综合久久不卡蜜臀| 亚洲一区日韩精品中文字幕| 91久久精品国产| 日韩黄色在线播放| 中文字幕 乱码 中文乱码91| 欧洲精品在线视频| 欧美高清一级片在线| 国产不卡一区| 欧美日韩综合色| 国产激情99| 日韩在线播放一区二区| 精品欧美久久久| 欧美日韩精品久久久| www..com日韩| 欧美不卡视频| 国产999在线观看| 高清1区2区| 日韩精品视频中文在线观看| 在线免费视频一区二区| 在线视频不卡一区二区| 91欧美在线视频| 国产日韩免费| 日韩视频专区| 日韩欧美色综合网站| 内射国产内射夫妻免费频道| 国产免费永久在线观看| 久久91精品久久久久久秒播| 99精品在线播放| 色综合天天性综合| 国产欧美久久久精品免费| 日韩高清在线不卡| 免费高清视频日韩| 久久精品久久精品国产大片| 国产日韩1区| 欧美日韩国产第一页| 日韩精品午夜| 99久久精品国产成人一区二区| 在线亚洲免费| 国产123在线| 亚洲一区在线视频观看| 久久69成人| 精品久久久久久综合日本欧美| 日韩免费视频一区二区视频在线观看| 欧美日韩国产中文精品字幕自在自线 | 亚洲成年网站在线观看| 欧美亚洲国产免费| 日韩区欧美区| 中文字幕精品国产| 精品在线99| 国产成人精品免费在线| 精品国产乱码久久久久久牛牛| 91精品999| 国产三级在线观看视频| 国产不卡在线观看视频| 蜜桃视频中文字幕| 国产一区在线观看视频| 精品日韩在线观看| 久久婷婷国产| 亚洲电影中文字幕在线观看| www.中文字幕在线| 欧美日韩高清在线| 日本熟女一区二区| 久久久综合av| 高清日韩中文字幕| 欧美日韩中文字幕在线| 中文字幕在线观看视频www| 中文字幕欧美亚洲| 亚洲电影中文字幕在线观看| 久久夜色精品国产欧美乱极品 | 日韩av一区在线| 91久久中文| 欧美婷婷精品激情| 精品一区二区在线观看视频| 欧美日韩中国免费专区在线看| 日韩福利视频导航| 国产一区久久| 国产欧美日韩中文字幕在线| 欧美日韩中文字幕日韩欧美| 久久久另类综合| 欧美日韩精品在线| 99色在线视频| 日韩中文字幕二区| 国产一区成人| 欧美久久久久久蜜桃| 精品激情国产视频| 黄色在线播放网站| 精品人妻一区二区免费视频| 亚洲高清中文字幕| 91精品日本| 日韩三级视频在线看| 中文字幕第一页在线播放| www.狠狠干| 亚洲午夜久久| 国产欧美日韩在线视频| 午夜精品免费视频| 91亚洲国产高清| 国产传媒久久久| 日韩欧美字幕| 国产高清精品在线观看| 999精品在线| 国产中文伊人| 在线免费91| 国产视频1区| 欧美99久久| 日韩亚洲欧美中文三级| 99国产一区| 日韩精品视频免费播放| 搞黄在线观看| 国产视频三级在线观看播放| 欧美日韩高清不卡| 欧美在线日韩精品| 中文字幕精品一区二| 国产伦精品免费视频| 日韩一级中文字幕| 久久99久久99精品中文字幕| 欧美日韩国产黄色| 亚洲国产午夜精品| 色综合久久六月婷婷中文字幕| 不卡在线视频| 国产三级在线| 国产福利免费在线观看| 91麻豆免费视频网站| 欧美一级在线免费| 免费看日韩精品| 精品熟女一区二区三区| 国产视频三级在线观看播放| 亚洲大片免费看| 欧美在线视频第一页| 视频一区二区不卡| 国产 欧美 日韩 在线| 日韩欧美久久| 日韩精品一级| 深夜福利亚洲| 亚洲综合三区| 国产欧美日韩中文字幕| 欧美国产91| 日韩一级网站| 91亚洲国产高清| 国产欧美日韩视频在线| 香蕉av一区| 国产对白在线| 精品三级av| 国产美女主播视频一区| 日韩精品视频在线播放| 亚洲成年人在线播放| 精品福利二区三区| 日韩中文在线字幕| 91国内精品在线视频| 亚洲欧美伊人| av亚洲免费| 国产黄色在线| 国产亚洲欧美中文| 亚洲成av人综合在线观看| 欧美日韩免费视频| 国产乱国产乱老熟300| 国产在线拍偷自揄拍精品| 日韩a一区二区| 国产日韩中文字幕在线| 中文字幕亚洲字幕| 在线一区av| 国产精久久久久| 国产欧美在线观看| 欧美日韩精品在线观看| av三级在线播放| 国产成人精品av久久| 九一久久久久久| 红桃视频亚洲| 国产黄色片大全| 日韩久久精品成人| 国产一区在线精品| 樱花草www在线| 91精品久久久久久久久久入口| 亚洲欧美伊人| 一区视频在线播放| 日韩精品视频在线观看视频| 亚洲一二三不卡| 中文字幕在线高清| 日韩精品在线视频| 国产在线黄色| 久久久久久99精品| 91精品视频在线| 欧美日韩激情在线一区二区三区| 日韩精品视频在线观看网址| 粉嫩喷白浆久久| 在线视频不卡一区二区| 国产黄色在线播放| 色猫猫国产区一区二在线视频 | 91精品国产91综合久久蜜臀| 国产婷婷一区二区| 精品久久蜜桃| 国产日韩精品在线| 欧美中文字幕在线观看视频| 樱花草www在线| 精品视频资源站| 日韩欧美高清在线| 欧美日韩国产一二| 亚洲一区视频在线观看视频| 日韩精品―中文字幕| 亚洲一区精品电影| 不卡在线一区二区| 精品一二三四区| 亚洲乱码视频| 中文在线视频观看| 国产午夜精品视频免费不卡69堂 | 中文字幕精品在线播放| 国产成人综合精品| 欧美二区在线观看| 国产95在线|亚洲| 五月综合激情日本mⅴ| 欧美在线日韩| 午夜一区二区三区免费| 国产不卡的av| 91精品在线视频观看| 久久蜜桃一区二区| 日韩精品在线免费观看| 日韩精品一级| 国产日韩精品电影| 一区免费视频| 国产成人精品网址| 欧美国产综合在线V| 99久久精品国产亚洲| 欧美三级一区二区三区| 在线免费播放av| a√免费观看在线网址www| 999在线视频| 深夜日韩欧美| www.尤物.com| 国产不卡在线观看视频| 一本一道久久a久久精品综合蜜臀| 日韩精品手机在线观看| 一级特黄aaa大片在线观看| 精品视频资源站| 日韩精品视频在线播放| 国产一区免费视频| av最新网址| 亚洲一区中文字幕在线| 色综合天天综合网天天狠天天 | 中文字幕在线视频日韩| 精品在线网站观看| 午夜国产在线| 精品久久九九| 欧美日韩在线播放三区四区| 一区二区三区高清在线| 91精品国产丝袜白色高跟鞋| 91精品国产综合久久久久| 不卡av免费在线| 国产嫩草影院久久久久| 欧美日韩精品免费| 在线不卡一区二区| 久本草在线中文字幕亚洲欧美| 国产欧美日韩不卡免费| 国产1卡2卡三卡四卡网站| 青青久在线视频免费观看| 国产在线黄色| 国产一级免费在线观看| 国产三级精品在线观看| 国产小视频免费在线观看| 中文字幕日韩在线观看| 黄色一区二区视频| 激情五月综合| 久久riav| www..com日韩| 中文在线日韩| 国产日韩在线亚洲字幕中文| 亚洲天堂国产视频| 成人精品视频一区| 国产一卡2卡3卡免费网站| 国产高清精品在线观看| 91精品国产91综合久久蜜臀| 最近中文字幕日韩精品| 日韩中文字幕91| 国产一级粉嫩xxxx| 午夜精品免费视频| 欧美日韩视频免费播放| av三级在线观看| 中文字幕欧美日韩va免费视频| 91久久精品国产91性色69| 99亚洲视频| 亚州福利视频| 亚洲素人一区二区| 国产欧美日韩中文久久| 在线欧美日韩国产| 日韩欧美中文在线视频| 77777_亚洲午夜久久多人| 久久精品免费看| 国产三级精品网站| 日韩精品视频在线免费观看| 一区二区三区精品99久久| 91精品久久久久| 在线一区二区三区精品| 麻豆精品99| 国产乱码午夜在线视频| 日韩在线视频中文字幕| 日韩精品高清不卡| 欧美日韩在线视频一区| 国产三级做爰在线观看| 国产成人一区二区精品非洲| 国产成人精品日本亚洲专区61| 日韩一级二级三级精品视频| 国产永久免费高清在线观看| 欧美日韩高清在线播放| 69av亚洲| 91精品国产免费| 欧美亚洲另类制服自拍| 国产黄色在线观看| 国内国产区免费视频| 91精品国产亚洲| 99精品一级欧美片免费播放| 91久久久精品| 国产不卡一区| 欧美久久久网站| 最新中文字幕在线| 中文字幕伊人| 久久精品卡一| av中文在线资源| 精品国产91| 国产黄在线播放| 国产一区二中文字幕在线看| 日韩欧美在线视频| 国产91久久久久蜜臀青青天草二| 91精品xxx在线观看| 日韩精品在线免费观看| 欧美日韩国产免费观看| 欧美不卡视频| 欧美日韩国产91| 91久久在线| 欧美三级网址| 不卡福利视频| 中文字幕在线官网|