本文已收錄至Github,推薦閱讀 ?? Java隨想錄
微信公眾號:Java隨想錄
這篇文章來講講快取,快取是優化網站性能的第一手段,目的是讓訪問速度更快,
說起快取,第一反應可能想到的就是Redis,目前比較好的方案是使用多級快取,如CPU→Ll/L2/L3→記憶體→磁盤就是一個典型的例子,CPU需要資料時先從L1讀取,如果沒有找到,則查找L2/L3讀取,如果沒有,則到記憶體中查找,如果還沒有,會到磁盤中查找,
目錄- 堆快取
- Nginx代理快取
- 多級快取
- 熱點Key自動探測
堆快取
使用Java堆記憶體來存盤快取物件,使用堆快取的好處是沒有序列化/反序列化,是最快的快取,缺點也很明顯,當快取的資料量很大時,GC(垃圾回收)暫停時間會變長,存盤容量受限于堆空間大小,一般通過軟參考/弱參考來存盤快取物件,即當堆記憶體不足時,可以強制回收這部分記憶體釋放堆記憶體空間,一般使用堆快取存盤較熱的資料,可以使用Caffeine Cache實作,
集成Caffeine Cache詳情見我另外一片文章:本地快取無冕之王Caffeine Cache
Nginx代理快取
Nginx 的Proxy Cache可以實作代理快取,特別需要說明的是,Proxy Cache機制依賴于Proxy Buffer機制,只有在Proxy Buffer機制開啟的情況下Proxy Cache的配置才發揮作用,Proxy Buffer默認是開啟的,快取內容將存放在tmpfs(記憶體檔案系統)以提升性能,
Proxy Buffer相關引數:
proxy_buffering on;
該引數設定是否開啟proxy的buffer功能,引數的值為on或者off,默認是on,
proxy_buffer_size 4k;
該引數用來設定一個特殊的buffer大小的,
從被代理服務器上獲取到的第一部分回應資料內容到代理服務器上,通常是header,就存到了這個buffer中,
如果該引數設定太小,會出現502錯誤碼,這是因為這部分buffer不夠存盤header資訊,建議設定為4k,
proxy_buffers 8 4k;
這個引數設定存盤被代理服務器上的資料所占用的buffer的個數和每個buffer的大小,
所有buffer的大小為這兩個數字的乘積,
proxy_busy_buffer_size 16k;
在所有的buffer里,我們需要規定一部分buffer把自己存的資料傳給服務器,這部分buffer就叫做busy_buffer,
proxy_busy_buffer_size引數用來設定處于busy狀態的buffer有多大,
proxy_temp_path
語法:proxy_temp_path path [level1 level2 level3]
定義proxy的臨時檔案存在目錄以及目錄的層級,
proxy_buffering 與 proxy_cache的區別:
緩沖(buffer)
- Nginx 將上游服務器的回應報文保存幾秒鐘,等整個接收之后,再發送給客戶端,
- 可以盡早與上游服務器斷開連接,減少其負載,但是會增加客戶端等待回應的時間,
- 如果不啟用緩沖,則 Nginx 收到上游服務器的一部分回應就會立即發送給客戶端,通信延遲低,
快取(cache)
- Nginx 將上游服務器的回應報文保存幾分鐘,當客戶端再次請求同一個回應報文時就直接回復,不必請求上游服務器,
- 可以避免重復向上游服務器請求一些固定不變的回應報文,減少上游服務器的負載,減少客戶端等待回應的時間,
Proxy Cache 相關的引數配置:
- proxy_cache_path:Nginx 使用該引數指定快取位置,有兩個必填引數, 第一個引數為快取目錄, 第二個引數keys_zone指定快取名稱和占用記憶體空間的大小,
- proxy_cache:該引數為之前指定的快取名稱,
- proxy_cache_key:該指令用來設定web快取的Key值,
- proxy_cache_min_uses:指定請求至少被發送了多少次以上時才快取,可以防止低頻請求被快取,
- proxy_cache_methods:指定哪些方法的請求被快取,
- proxy_cache_valid:該指令用于對于不同回傳狀態碼的URL設定不同的快取時間,
- proxy_cache_bypass:指定Nginx使用快取的條件,
如下示例:
http {
proxy_cache_path /data/nginx/cache keys_zone=one:10m max_size=10g;
proxy_cache_methods GET HEAD POST PUT;
proxy_cache_min_uses 1;
proxy_cache_valid 200 302 10m;
proxy_cache_key "$host:$server_port$uri$is_args$args";
upstream www.baidu.com {
server 127.0.0.1:8880;
server 127.0.0.1:8881;
server 127.0.0.1:8882;
}
server {
listen 80 ;
proxy_cache one ;
server_name www.baidu.com;
location / {
proxy_pass http://www.baidu.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_cache_bypass $cookie_nocache $arg_nocache $arg_comment ;
}
}
}
多級快取
Nginx快取→分布式Redis快取(可以使用Lua腳本直接在Nginx里讀取Redis)→堆記憶體
-
接入 Nginx將請求負載均衡到應用Nginx,此處常用的負載均衡演算法是輪詢或者一致性哈希,輪詢可以使服務器的請求更加均衡,而一致性哈希可以提升應用Nginx的快取命中率,
-
應用Nginx讀取本地快取(本地快取可以使用Lua Shared Dict,Nginx Proxy Cache(磁盤/記憶體)、Local Redis實作),如果本地快取命中,則直接回傳,使用應用Nginx本地快取可以提升整體的吞吐量,降低后端壓力,尤其應對熱點問題非常有效,
-
如果Nginx本地快取沒命中,則會讀取相應的分布式快取(如 Redis快取,還可以考慮使用主從架構來提升性能和吞吐量),如果分布式快取命中,則直接回傳相應資料(并回寫到Nginx本地快取),
-
如果分布式快取也沒有命中,則會回源到Tomcat集群,在回源到Tomcat 集群時,也可以使用輪詢和一致性哈希作為負載均衡演算法,
-
在 Tomcat應用中,首先讀取本地堆快取,如果有,則直接回傳(并會寫到主Redis集群),
-
作為可選部分,如果步驟4沒有命中,則可以再嘗試一次讀主Redis集群操作,目的是防止當從集群有問題時的流量沖擊,
-
如果所有快取都沒有命中,則只能查詢DB或相關服務獲取相關資料并回傳,
-
步驟7回傳的資料異步寫到主Redis集群,
整體分了三部分快取:應用Nginx本地快取、分布式快取、Tomcat堆快取,每一層快取都用來解決相關問題,如應用Nginx本地快取用來解決熱點快取問題,分布式快取用來減少訪問回源率,Tomcat堆快取用于防止相關快取失效/崩潰之后的沖擊,
熱點Key自動探測
熱點資料其實可以分為:靜態熱點資料 和 動態熱點資料,
所謂“靜態熱點資料”,就是能夠提前預測的熱點資料,例如,我們可以通過賣家報名的方式提前篩選出來,通過報名系統對這些熱點商品進行打標,另外,我們還可以通過大資料分析來提前發現熱點商品,比如我們分析歷史成交記錄、用戶的購物車記錄,來發現哪些商品可能更熱門、更好賣,這些都是可以提前分析出來的熱點,分析出來之后,我們可以提前放入快取中保護起來,這在技術上是可以實作的,
所謂“動態熱點資料”,就是不能被提前預測到的,系統在運行程序中臨時產生的熱點,例如,賣家在抖音上做了廣告,然后商品一下就火了,導致它在短時間內被大量購買,
所以如何動態預測熱點資料就成了我們快取系統的關鍵,
這里我給出一個動態熱點發現系統的具體實作,
-
構建一個異步的系統,它可以收集交易鏈路上各個環節中的中間件產品的熱點Key,如Nginx、快取、RPC服務框架等這些中間件(一些中間件產品本身已經有熱點統計模塊),
-
建立一個熱點上報和可以按照需求訂閱的熱點服務的下發規范,主要目的是通過交易鏈路上各個系統(包括詳情、購物車、交易、優惠、庫存、物流等)訪問的時間差把上游已經發現的熱點透傳給下游系統,提前做好保護,比如,對于大促高峰期,詳情系統是最早知道的,在統一接入層上 Nginx模塊統計的熱點URL,
-
將上游系統收集的熱點資料發送到熱點服務臺,然后下游系統(如交易系統)就會知道哪些商品會被頻繁呼叫,然后做熱點保護,對于熱點的統計可以很簡單的對訪問的商品進行訪問計數,然后排序還有就是用通常的佇列的淘汰演算法如lru等都可以實作,
實作思路步驟如下:
1.接入Nginx將請求轉發給應用Nginx,
2.應用 Nginx首先讀取本地快取,如果命中,則直接回傳,不命中會讀取分布式快取、回源到Tomcat進行處理,
3.應用Nginx 會將請求上報給實時熱點發現系統,如使用UDP直接上報請求,或者將請求寫到本地kafka,或者使用flume訂閱本地 Nginx日志,上報給實時熱點發現系統后,它將進行熱點統計(可以考慮storm實時計算),
4.根據設定的閾值將熱點資料推送到應用Nginx本地快取,
我們主要是依賴前面的導購頁面(包括首頁、搜索頁面、商品詳情、購物車等)提前識別哪些商品的訪問量高,通過這些系統中的中間件來收集熱點資料,并記錄到日志中,
我們通過部署在每臺機器上的Agent把日志匯總到聚合和分析集群中,然后把符合一定規則的熱點資料,通過訂閱分發系統再推送到相應的系統中,你可以是把熱點資料填充到Cache中,或者直接推送到應用服務器的記憶體中,還可以對這些資料進行攔截,總之下游系統可以訂閱這些資料,然后根據自己的需求決定如何處理這些資料,
熱點發現要做到接近實時(3s內完成熱點資料的發現),因為只有做到接近實時,動態發現才有意義,才能實時地對下游系統提供保護,如果10s內才發送熱點就沒意義了,因為10s內用戶可以進行的操作太多了,時間越長,不可控元素越多,熱點快取命中率越低,
京東在網上開源了熱點key探測技術的具體實作:https://gitee.com/jd-platform-opensource/hotkey?_from=gitee_search
本篇文章就到這里,感謝閱讀,如果本篇博客有任何錯誤和建議,歡迎給我留言指正,文章持續更新,可以關注公眾號第一時間閱讀,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/545042.html
標籤:其他
上一篇:【牛客】4 序列檢測&時序邏輯
