1. 程式人生 > >隨心的專欄

隨心的專欄

1.簡介

定義:快取是在計算機上的一個原始資料的複製集,以便於訪問。

快取在計算機系統中被廣泛應用,從快取的定義來看,快取是計算機上的原始資料的複製集,因此對於快取的使用與應用場景密切相關,在不同的場景上會有不同的意義。

1.1 Web應用快取分類

  1. 客戶端快取 對於BS架構的網際網路應用來說客戶端快取主要分為頁面快取和瀏覽器快取兩種,對於APP而言主要是自身所使用的快取。 【瀏覽器快取根據一套與伺服器約定的規則進行工作,在同一個會話過程中會檢查一次並確定快取的副本足夠新。如果你瀏覽過程中,比如前進或後退,訪問到同一個圖片,這些圖片可以從瀏覽器快取中調出而即時顯現。瀏覽器快取根據一套與伺服器約定的規則進行工作,在同一個會話過程中會檢查一次並確定快取的副本足夠新。如果你瀏覽過程中,比如前進或後退,訪問到同一個圖片,這些圖片可以從瀏覽器快取中調出而即時顯現。】

  2. 網路中快取 網路中的快取主要是指代理伺服器對客戶端請求資料的快取,主要分為WEB代理快取和邊緣快取(CDN邊緣快取)

  3. 服務端快取 對於服務端快取而言,從系統的架構上面區分可以將快取分為:伺服器本地快取(localCache),分散式快取(Redis、Memcached等nosql)和資料庫快取。

    3.1 伺服器本地快取 本地快取是一級快取,位於服務本機的記憶體中,在操作本地快取的時候不需要網路IO不需要檔案IO,直接從本機記憶體中讀取資料,因此讀寫速度最快。 本地快取存在的問題:     本地快取資料直接儲存在JVM中,需要考慮快取資料的大小、JVM的垃圾回收效能消耗;     單服務是叢集部署的時候,應該考慮是否需要做叢集中本地快取的資料同步 在實際的開發中可以自己實現簡單的本地快取也可以使用開源的本地快取框架,比如:ehcache、JBoss Cache等

    3.2分散式快取     當本地快取被穿透的時候就會去查詢分散式快取,當在分散式快取中查詢到資料的時候,直接將查詢結果放到本地快取中。     對於分散式快取主要是使用NoSQL資料庫來實現,常用的NoSQL資料庫有Redis、Memcached、MongoDB等。目前比較流行的Redis來說,支援Slava/Master模式和Cluster

    3.3 資料庫快取     資料庫在設計的時候也有快取操作,更改相關引數開啟查詢快取

1.2 快取中的幾個常用術語

  1. 快取命中:當客戶端請求的資料在快取中,這個快取中的資料就會被使用,這一行為被稱為快取命中
  2. 沒有命中:快取中沒有查詢到資料,並且資料庫中可以查到此資料,並將資料放到快取中
  3. 快取穿透:是指查詢一個快取中一定不存在的資料。即快取中不存在,並且資料庫中也不存在,並且在資料庫中沒有查詢到資料的情況下,不會去寫快取,這樣就導致每次對於此資料的查詢都會去查詢資料庫,這樣就導致快取失去了意義。對於如何解決快取穿透問題,後面會具體分析。
  4. 儲存成本:快取沒有命中的時候,從其他資料來源取出資料並放到快取中的時間成本和空間成本就是儲存成本。
  5. 快取失效:當快取中的資料已經更新時,則此資料已經失效
  6. 替代策略:當快取沒有命中的時,並且快取容量已滿,就需要在快取中去除一條舊資料,然後加入一條新資料,而應該去除哪些資料,就由替代策略來決定。 常用的替代策略有:LRU、LFU等。在使用快取演算法的時候,通常會考慮使用頻率、獲取成本、快取容量和時間等因素。
  7. 快取雪崩:如果快取集中在一段時間內失效,發生大量的快取穿透,所有的查詢都落在資料庫上,造成了快取雪崩。

1.3 快取設計需要解決以下幾個問題

  1. 快取什麼? 哪些資料需要快取:1.熱點資料;2.靜態資源。

  2. 快取的位置? CDN,反向代理,分散式快取伺服器,本機(記憶體,硬碟)

  3. 如何快取的問題?

  4. 過期策略 固定時間:比如指定快取的時間是30分鐘; 相對時間:比如最近10分鐘內沒有訪問的資料;

  5. 同步機制 實時寫入;(推) 非同步重新整理;(推拉)

2.客戶端快取之HTT快取

2.1 HTTP報文

HTTP報文就是瀏覽器和伺服器間通訊時傳送及響應的資料塊。 瀏覽器向伺服器請求資料,傳送請求(request)報文;伺服器向瀏覽器返回資料,返回響應(response)報文。 報文資訊主要分為4部分

  1. 請求行
  2. 空行
  3. 包含屬性的首部(header)--------------------------附加資訊(cookie,快取資訊等)與快取相關的規則資訊,均包含在header中
  4. 包含資料的主體部分(body)-----------------------HTTP請求真正想要傳輸的部分

基本形式:

GET / HTTP/1.1
Host: www.enjoytoday.cn
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://www.enjoytoday.cn/posts/326
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1466032270994; UM_distinctid=15c4ef2ac4e2e4-0d13269271b947-1b2a120b-1fa400-15c4ef2ac4f7b5; un=aGZjYWk=; comment_author=aGZjYWk=; [email protected]; comment_author_url=http://www.enjoytoday.cn; c_id=dUhIaTlndmc4MVVYbjRQTGxMRTotMTpFODg3QjgzQjg1NjgxQjQxRUYxNjg2QzJFRkMyQjI2QQ==; JSESSIONID=ADBC8C3DADF6C815D778450C193C6637.ajp13_worker; Hm_lvt_ce55bfda158556585a8b7b246346c8ba=1498560244,1498739070,1498833193,1498917432; Hm_lpvt_ce55bfda158556585a8b7b246346c8ba=1498917597; CNZZDATA1262047894=1598545996-1495973145-%7C1498917578

username=hfcai&sex=man

2.2 快取規則解析

為方便大家理解,我們認為瀏覽器存在一個快取資料庫,用於儲存快取資訊。

在客戶端第一次請求資料時,此時快取資料庫中沒有對應的快取資料,需要請求伺服器,伺服器返回後,將資料儲存至快取資料庫中。

在這裡插入圖片描述

HTTP快取有多種規則,根據是否需要重新向伺服器發起請求來分類,我將其分為兩大類(強制快取,對比快取)

在詳細介紹這兩種規則之前,先通過時序圖的方式,讓大家對這兩種規則有個簡單瞭解。

已存在快取資料時,僅基於強制快取,請求資料的流程如下 在這裡插入圖片描述

已存在快取資料時,僅基於對比快取,請求資料的流程如下 在這裡插入圖片描述

對快取機制不太瞭解的同學可能會問,基於對比快取的流程下,不管是否使用快取,都需要向伺服器傳送請求,那麼還用快取幹什麼?

這個問題,我們暫且放下,後文在詳細介紹每種快取規則的時候,會帶給大家答案。

我們可以看到兩類快取規則的不同,強制快取如果生效,不需要再和伺服器發生互動,而對比快取不管是否生效,都需要與服務端發生互動。

兩類快取規則可以同時存在,強制快取優先順序高於對比快取,也就是說,當執行強制快取的規則時,如果快取生效,直接使用快取,不再執行對比快取規則。

2.3 強制快取

從上文我們得知,強制快取,在快取資料未失效的情況下,可以直接使用快取資料,那麼瀏覽器是如何判斷快取資料是否失效呢? 我們知道,在沒有快取資料的時候,瀏覽器向伺服器請求資料時,伺服器會將資料和快取規則一併返回,快取規則資訊包含在響應header中。

對於強制快取來說,響應header中會有兩個欄位來標明失效規則***(Expires/Cache-Control)*** 使用chrome的開發者工具,可以很明顯的看到對於強制快取生效時,網路請求的情況 在這裡插入圖片描述

Expires   Expires的值為服務端返回的到期時間,即下一次請求時,請求時間小於服務端返回的到期時間,直接使用快取資料。   不過Expires 是HTTP 1.0的東西,現在預設瀏覽器均預設使用HTTP 1.1,所以它的作用基本忽略。   另一個問題是,到期時間是由服務端生成的,但是客戶端時間可能跟服務端時間有誤差,這就會導致快取命中的誤差。 所以HTTP 1.1 的版本,使用Cache-Control替代。

Cache-Control Cache-Control 是最重要的規則。常見的取值有private、public、no-cache、max-age,no-store,預設為private。

  • private: 客戶端可以快取
  • public: 客戶端和代理伺服器都可快取(前端的同學,可以認為public和private是一樣的)
  • max-age=xxx: 快取的內容將在 xxx 秒後失效
  • no-cache: 需要使用對比快取來驗證快取資料(後面介紹)
  • no-store: 所有內容都不會快取,強制快取,對比快取都不會觸發(對於前端開發來說,快取越多越好,so…基本上和它說886)

在這裡插入圖片描述

圖中Cache-Control僅指定了max-age,所以預設為private,快取時間為31536000秒(365天)

也就是說,在365天內再次請求這條資料,都會直接獲取快取資料庫中的資料,直接使用。

2.4 對比快取

對比快取,顧名思義,需要進行比較判斷是否可以使用快取。 瀏覽器第一次請求資料時,伺服器會將快取標識與資料一起返回給客戶端,客戶端將二者備份至快取資料庫中。

再次請求資料時,客戶端將備份的快取標識傳送給伺服器,伺服器根據快取標識進行判斷,判斷成功後,返回304狀態碼,通知客戶端比較成功,可以使用快取資料。

在這裡插入圖片描述

通過兩圖的對比,我們可以很清楚的發現,在對比快取生效時,狀態碼為304,並且報文大小和請求時間大大減少。 原因是,服務端在進行標識比較後,只返回header部分,通過狀態碼通知客戶端使用快取,不再需要將報文主體部分返回給客戶端。

對於對比快取來說,快取標識的傳遞是我們著重需要理解的,它在請求header和響應header間進行傳遞, 一共分為兩種標識傳遞,接下來,我們分開介紹。

Last-Modified / If-Modified-Since Last-Modified:

伺服器在響應請求時,告訴瀏覽器資源的最後修改時間。 在這裡插入圖片描述

If-Modified-Since:

再次請求伺服器時,通過此欄位通知伺服器上次請求時,伺服器返回的資源最後修改時間。

伺服器收到請求後發現有頭If-Modified-Since 則與被請求資源的最後修改時間進行比對。

若資源的最後修改時間大於If-Modified-Since,說明資源又被改動過,則響應整片資源內容,返回狀態碼200;

若資源的最後修改時間小於或等於If-Modified-Since,說明資源無新修改,則響應HTTP 304,告知瀏覽器繼續使用所儲存的cache。 在這裡插入圖片描述

Etag / If-None-Match(優先順序高於Last-Modified / If-Modified-Since)

Etag:

伺服器響應請求時,告訴瀏覽器當前資源在伺服器的唯一標識(生成規則由伺服器決定)。 在這裡插入圖片描述

If-None-Match:

再次請求伺服器時,通過此欄位通知伺服器客戶段快取資料的唯一標識。

伺服器收到請求後發現有頭If-None-Match 則與被請求資源的唯一標識進行比對,

不同,說明資源又被改動過,則響應整片資源內容,返回狀態碼200;

相同,說明資源無新修改,則響應HTTP 304,告知瀏覽器繼續使用所儲存的cache。

在這裡插入圖片描述

2.5 總結

對於強制快取,伺服器通知瀏覽器一個快取時間,在快取時間內,下次請求,直接用快取,不在時間內,執行比較快取策略。 對於比較快取,將快取資訊中的Etag和Last-Modified通過請求傳送給伺服器,由伺服器校驗,返回304狀態碼時,瀏覽器直接使用快取。

在這裡插入圖片描述

在這裡插入圖片描述

【附:HTTP狀態碼】 在這裡插入圖片描述

在這裡插入圖片描述

狀態碼 含義

  • 100 客戶端應當繼續傳送請求。這個臨時響應是用來通知客戶端它的部分請求已經被伺服器接收,且仍未被拒絕。客戶端應當繼續傳送請求的剩餘部分,或者如果請求已經完成,忽略這個響應。伺服器必須在請求完成後向客戶端傳送一個最終響應。

  • 101 伺服器已經理解了客戶端的請求,並將通過Upgrade 訊息頭通知客戶端採用不同的協議來完成這個請求。在傳送完這個響應最後的空行後,伺服器將會切換到在Upgrade 訊息頭中定義的那些協議。   只有在切換新的協議更有好處的時候才應該採取類似措施。例如,切換到新的HTTP 版本比舊版本更有優勢,或者切換到一個實時且同步的協議以傳送利用此類特性的資源。

  • 102 由WebDAV(RFC 2518)擴充套件的狀態碼,代表處理將被繼續執行。

  • 200 請求已成功,請求所希望的響應頭或資料體將隨此響應返回。

  • 201 請求已經被實現,而且有一個新的資源已經依據請求的需要而建立,且其 URI 已經隨Location 頭資訊返回。假如需要的資源無法及時建立的話,應當返回 ‘202 Accepted’。

  • 202 伺服器已接受請求,但尚未處理。正如它可能被拒絕一樣,最終該請求可能會也可能不會被執行。在非同步操作的場合下,沒有比傳送這個狀態碼更方便的做法了。   返回202狀態碼的響應的目的是允許伺服器接受其他過程的請求(例如某個每天只執行一次的基於批處理的操作),而不必讓客戶端一直保持與伺服器的連線直到批處理操作全部完成。在接受請求處理並返回202狀態碼的響應應當在返回的實體中包含一些指示處理當前狀態的資訊,以及指向處理狀態監視器或狀態預測的指標,以便使用者能夠估計操作是否已經完成。

  • 203 伺服器已成功處理了請求,但返回的實體頭部元資訊不是在原始伺服器上有效的確定集合,而是來自本地或者第三方的拷貝。當前的資訊可能是原始版本的子集或者超集。例如,包含資源的元資料可能導致原始伺服器知道元資訊的超級。使用此狀態碼不是必須的,而且只有在響應不使用此狀態碼便會返回200 OK的情況下才是合適的。

  • 204 伺服器成功處理了請求,但不需要返回任何實體內容,並且希望返回更新了的元資訊。響應可能通過實體頭部的形式,返回新的或更新後的元資訊。如果存在這些頭部資訊,則應當與所請求的變數相呼應。   如果客戶端是瀏覽器的話,那麼使用者瀏覽器應保留髮送了該請求的頁面,而不產生任何文件檢視上的變化,即使按照規範新的或更新後的元資訊應當被應用到使用者瀏覽器活動檢視中的文件。   由於204響應被禁止包含任何訊息體,因此它始終以訊息頭後的第一個空行結尾。

  • 205 伺服器成功處理了請求,且沒有返回任何內容。但是與204響應不同,返回此狀態碼的響應要求請求者重置文件檢視。該響應主要是被用於接受使用者輸入後,立即重置表單,以便使用者能夠輕鬆地開始另一次輸入。   與204響應一樣,該響應也被禁止包含任何訊息體,且以訊息頭後的第一個空行結束。

  • 206 伺服器已經成功處理了部分 GET 請求。類似於 FlashGet 或者迅雷這類的 HTTP 下載工具都是使用此類響應實現斷點續傳或者將一個大文件分解為多個下載段同時下載。   該請求必須包含 Range 頭資訊來指示客戶端希望得到的內容範圍,並且可能包含 If-Range 來作為請求條件。   響應必須包含如下的頭部域:   Content-Range 用以指示本次響應中返回的內容的範圍;如果是 Content-Type 為 multipart/byteranges 的多段下載,則每一 multipart 段中都應包含 Content-Range 域用以指示本段的內容範圍。假如響應中包含 Content-Length,那麼它的數值必須匹配它返回的內容範圍的真實位元組數。 Date   ETag 和/或 Content-Location,假如同樣的請求本應該返回200響應。   Expires, Cache-Control,和/或 Vary,假如其值可能與之前相同變數的其他響應對應的值不同的話。   假如本響應請求使用了 If-Range 強快取驗證,那麼本次響應不應該包含其他實體頭;假如本響應的請求使用了 If-Range 弱快取驗證,那麼本次響應禁止包含其他實體頭;這避免了快取的實體內容和更新了的實體頭資訊之間的不一致。否則,本響應就應當包含所有本應該返回200響應中應當返回的所有實體頭部域。   假如 ETag 或 Last-Modified 頭部不能精確匹配的話,則客戶端快取應禁止將206響應返回的內容與之前任何快取過的內容組合在一起。   任何不支援 Range 以及 Content-Range 頭的快取都禁止快取206響應返回的內容。

  • 207 由WebDAV(RFC 2518)擴充套件的狀態碼,代表之後的訊息體將是一個XML訊息,並且可能依照之前子請求數量的不同,包含一系列獨立的響應程式碼。 300 被請求的資源有一系列可供選擇的回饋資訊,每個都有自己特定的地址和瀏覽器驅動的商議資訊。使用者或瀏覽器能夠自行選擇一個首選的地址進行重定向。   除非這是一個 HEAD 請求,否則該響應應當包括一個資源特性及地址的列表的實體,以便使用者或瀏覽器從中選擇最合適的重定向地址。這個實體的格式由 Content-Type 定義的格式所決定。瀏覽器可能根據響應的格式以及瀏覽器自身能力,自動作出最合適的選擇。當然,RFC 2616規範並沒有規定這樣的自動選擇該如何進行。 如果伺服器本身已經有了首選的回饋選擇,那麼在 Location 中應當指明這個回饋的 URI;瀏覽器可能會將這個 Location 值作為自動重定向的地址。此外,除非額外指定,否則這個響應也是可快取的。

  • 301 被請求的資源已永久移動到新位置,並且將來任何對此資源的引用都應該使用本響應返回的若干個 URI 之一。如果可能,擁有連結編輯功能的客戶端應當自動把請求的地址修改為從伺服器反饋回來的地址。除非額外指定,否則這個響應也是可快取的。   新的永久性的 URI 應當在響應的 Location 域中返回。除非這是一個 HEAD 請求,否則響應的實體中應當包含指向新的 URI 的超連結及簡短說明。   如果這不是一個 GET 或者 HEAD 請求,因此瀏覽器禁止自動進行重定向,除非得到使用者的確認,因為請求的條件可能因此發生變化。   注意:對於某些使用 HTTP/1.0 協議的瀏覽器,當它們傳送的 POST 請求得到了一個301響應的話,接下來的重定向請求將會變成 GET 方式。

  • 302 請求的資源現在臨時從不同的 URI 響應請求。由於這樣的重定向是臨時的,客戶端應當繼續向原有地址傳送以後的請求。只有在Cache-Control或Expires中進行了指定的情況下,這個響應才是可快取的。   新的臨時性的 URI 應當在響應的 Location 域中返回。除非這是一個 HEAD 請求,否則響應的實體中應當包含指向新的 URI 的超連結及簡短說明。   如果這不是一個 GET 或者 HEAD 請求,那麼瀏覽器禁止自動進行重定向,除非得到使用者的確認,因為請求的條件可能因此發生變化。   注意:雖然RFC 1945和RFC 2068規範不允許客戶端在重定向時改變請求的方法,但是很多現存的瀏覽器將302響應視作為303響應,並且使用 GET 方式訪問在 Location 中規定的 URI,而無視原先請求的方法。狀態碼303和307被添加了進來,用以明確伺服器期待客戶端進行何種反應。

  • 303 對應當前請求的響應可以在另一個 URI 上被找到,而且客戶端應當採用 GET 的方式訪問那個資源。這個方法的存在主要是為了允許由指令碼啟用的POST請求輸出重定向到一個新的資源。這個新的 URI 不是原始資源的替代引用。同時,303響應禁止被快取。當然,第二個請求(重定向)可能被快取。 新的 URI 應當在響應的 Location 域中返回。除非這是一個 HEAD 請求,否則響應的實體中應當包含指向新的 URI 的超連結及簡短說明。   注意:許多 HTTP/1.1 版以前的 瀏覽器不能正確理解303狀態。如果需要考慮與這些瀏覽器之間的互動,302狀態碼應該可以勝任,因為大多數的瀏覽器處理302響應時的方式恰恰就是上述規範要求客戶端處理303響應時應當做的。

  • 304 如果客戶端傳送了一個帶條件的 GET 請求且該請求已被允許,而文件的內容(自上次訪問以來或者根據請求的條件)並沒有改變,則伺服器應當返回這個狀態碼。304響應禁止包含訊息體,因此始終以訊息頭後的第一個空行結尾。   該響應必須包含以下的頭資訊:   Date,除非這個伺服器沒有時鐘。假如沒有時鐘的伺服器也遵守這些規則,那麼代理伺服器以及客戶端可以自行將 Date 欄位新增到接收到的響應頭中去(正如RFC 2068中規定的一樣),快取機制將會正常工作。   ETag 和/或 Content-Location,假如同樣的請求本應返回200響應。   Expires, Cache-Control,和/或Vary,假如其值可能與之前相同變數的其他響應對應的值不同的話。   假如本響應請求使用了強快取驗證,那麼本次響應不應該包含其他實體頭;否則(例如,某個帶條件的 GET 請求使用了弱快取驗證),本次響應禁止包含其他實體頭;這避免了快取了的實體內容和更新了的實體頭資訊之間的不一致。   假如某個304響應指明瞭當前某個實體沒有快取,那麼快取系統必須忽視這個響應,並且重複傳送不包含限制條件的請求。   假如接收到一個要求更新某個快取條目的304響應,那麼快取系統必須更新整個條目以反映所有在響應中被更新的欄位的值。

  • 305 被請求的資源必須通過指定的代理才能被訪問。Location 域中將給出指定的代理所在的 URI 資訊,接收者需要重複傳送一個單獨的請求,通過這個代理才能訪問相應資源。只有原始伺服器才能建立305響應。   注意:RFC 2068中沒有明確305響應是為了重定向一個單獨的請求,而且只能被原始伺服器建立。忽視這些限制可能導致嚴重的安全後果。

  • 306 在最新版的規範中,306狀態碼已經不再被使用。

  • 307 請求的資源現在臨時從不同的URI 響應請求。由於這樣的重定向是臨時的,客戶端應當繼續向原有地址傳送以後的請求。只有在Cache-Control或Expires中進行了指定的情況下,這個響應才是可快取的。   新的臨時性的URI 應當在響應的 Location 域中返回。除非這是一個HEAD 請求,否則響應的實體中應當包含指向新的URI 的超連結及簡短說明。因為部分瀏覽器不能識別307響應,因此需要新增上述必要資訊以便使用者能夠理解並向新的 URI 發出訪問請求。   如果這不是一個GET 或者 HEAD 請求,那麼瀏覽器禁止自動進行重定向,除非得到使用者的確認,因為請求的條件可能因此發生變化。

  • 400 1、語義有誤,當前請求無法被伺服器理解。除非進行修改,否則客戶端不應該重複提交這個請求。   2、請求引數有誤。

  • 401 當前請求需要使用者驗證。該響應必須包含一個適用於被請求資源的 WWW-Authenticate 資訊頭用以詢問使用者資訊。客戶端可以重複提交一個包含恰當的 Authorization 頭資訊的請求。如果當前請求已經包含了 Authorization 證書,那麼401響應代表著伺服器驗證已經拒絕了那些證書。如果401響應包含了與前一個響應相同的身份驗證詢問,且瀏覽器已經至少嘗試了一次驗證,那麼瀏覽器應當向用戶展示響應中包含的實體資訊,因為這個實體資訊中可能包含了相關診斷資訊。參見RFC 2617。

  • 402 該狀態碼是為了將來可能的需求而預留的。

  • 403 伺服器已經理解請求,但是拒絕執行它。與401響應不同的是,身份驗證並不能提供任何幫助,而且這個請求也不應該被重複提交。如果這不是一個 HEAD 請求,而且伺服器希望能夠講清楚為何請求不能被執行,那麼就應該在實體內描述拒絕的原因。當然伺服器也可以返回一個404響應,假如它不希望讓客戶端獲得任何資訊。

  • 404 請求失敗,請求所希望得到的資源未被在伺服器上發現。沒有資訊能夠告訴使用者這個狀況到底是暫時的還是永久的。假如伺服器知道情況的話,應當使用410狀態碼來告知舊資源因為某些內部的配置機制問題,已經永久的不可用,而且沒有任何可以跳轉的地址。404這個狀態碼被廣泛應用於當伺服器不想揭示到底為何請求被拒絕或者沒有其他適合的響應可用的情況下。

  • 405 請求行中指定的請求方法不能被用於請求相應的資源。該響應必須返回一個Allow 頭資訊用以表示出當前資源能夠接受的請求方法的列表。   鑑於 PUT,DELETE 方法會對伺服器上的資源進行寫操作,因而絕大部分的網頁伺服器都不支援或者在預設配置下不允許上述請求方法,對於此類請求均會返回405錯誤。

  • 406 請求的資源的內容特性無法滿足請求頭中的條件,因而無法生成響應實體。   除非這是一個 HEAD 請求,否則該響應就應當返回一個包含可以讓使用者或者瀏覽器從中選擇最合適的實體特性以及地址列表的實體。實體的格式由 Content-Type 頭中定義的媒體型別決定。瀏覽器可以根據格式及自身能力自行作出最佳選擇。但是,規範中並沒有定義任何作出此類自動選擇的標準。

  • 407  與401響應類似,只不過客戶端必須在代理伺服器上進行身份驗證。代理伺服器必須返回一個 Proxy-Authenticate 用以進行身份詢問。客戶端可以返回一個 Proxy-Authorization 資訊頭用以驗證。參見RFC 2617。

  • 408 請求超時。客戶端沒有在伺服器預備等待的時間內完成一個請求的傳送。客戶端可以隨時再次提交這一請求而無需進行任何更改。

  • 409 由於和被請求的資源的當前狀態之間存在衝突,請求無法完成。這個程式碼只允許用在這樣的情況下才能被使用:使用者被認為能夠解決衝突,並且會重新提交新的請求。該響應應當包含足夠的資訊以便使用者發現衝突的源頭。   衝突通常發生於對 PUT 請求的處理中。例如,在採用版本檢查的環境下,某次 PUT 提交的對特定資源的修改請求所附帶的版本資訊與之前的某個(第三方)請求向衝突,那麼此時伺服器就應該返回一個409錯誤,告知使用者請求無法完成。此時,響應實體中很可能會包含兩個衝突版本之間的差異比較,以便使用者重新提交歸併以後的新版本。

  • 410 被請求的資源在伺服器上已經不再可用,而且沒有任何已知的轉發地址。這樣的狀況應當被認為是永久性的。如果可能,擁有連結編輯功能的客戶端應當在獲得使用者許可後刪除所有指向這個地址的引用。如果伺服器不知道或者無法確定這個狀況是否是永久的,那麼就應該使用404狀態碼。除非額外說明,否則這個響應是可快取的。   410響應的目的主要是幫助網站管理員維護網站,通知使用者該資源已經不再可用,並且伺服器擁有者希望所有指向這個資源的遠端連線也被刪除。這類事件在限時、增值服務中很普遍。同樣,410響應也被用於通知客戶端在當前伺服器站點上,原本屬於某個個人的資源已經不再可用。當然,是否需要把所有永久不可用的資源標記為’410 Gone’,以及是否需要保持此標記多長時間,完全取決於伺服器擁有者。

  • 411 伺服器拒絕在沒有定義 Content-Length 頭的情況下接受請求。在添加了表明請求訊息體長度的有效 Content-Length 頭之後,客戶端可以再次提交該請求。

  • 412 伺服器在驗證在請求的頭欄位中給出先決條件時,沒能滿足其中的一個或多個。這個狀態碼允許客戶端在獲取資源時在請求的元資訊(請求頭欄位資料)中設定先決條件,以此避免該請求方法被應用到其希望的內容以外的資源上。

  • 413 伺服器拒絕處理當前請求,因為該請求提交的實體資料大小超過了伺服器願意或者能夠處理的範圍。此種情況下,伺服器可以關閉連線以免客戶端繼續傳送此請求。   如果這個狀況是臨時的,伺服器應當返回一個 Retry-After 的響應頭,以告知客戶端可以在多少時間以後重新嘗試。

  • 414 請求的URI 長度超過了伺服器能夠解釋的長度,因此伺服器拒絕對該請求提供服務。這比較少見,通常的情況包括:   本應使用POST方法的表單提交變成了GET方法,導致查詢字串(Query String)過長。   重定向URI “黑洞”,例如每次重定向把舊的 URI 作為新的 URI 的一部分,導致在若干次重定向後 URI 超長。   客戶端正在嘗試利用某些伺服器中存在的安全漏洞攻擊伺服器。這類伺服器使用固定長度的緩衝讀取或操作請求的 URI,當 GET 後的引數超過某個數值後,可能會產生緩衝區溢位,導致任意程式碼被執行[1]。沒有此類漏洞的伺服器,應當返回414狀態碼。

  • 415 對於當前請求的方法和所請求的資源,請求中提交的實體並不是伺服器中所支援的格式,因此請求被拒絕。 416 如果請求中包含了 Range 請求頭,並且 Range 中指定的任何資料範圍都與當前資源的可用範圍不重合,同時請求中又沒有定義 If-Range 請求頭,那麼伺服器就應當返回416狀態碼。   假如 Range 使用的是位元組範圍,那麼這種情況就是指請求指定的所有資料範圍的首位元組位置都超過了當前資源的長度。伺服器也應當在返回416狀態碼的同時,包含一個 Content-Range 實體頭,用以指明當前資源的長度。這個響應也被禁止使用 multipart/byteranges 作為其 Content-Type。

  • 417 在請求頭 Expect 中指定的預期內容無法被伺服器滿足,或者這個伺服器是一個代理伺服器,它有明顯的證據證明在當前路由的下一個節點上,Expect 的內容無法被滿足。 421 從當前客戶端所在的IP地址到伺服器的連線數超過了伺服器許可的最大範圍。通常,這裡的IP地址指的是從伺服器上看到的客戶端地址(比如使用者的閘道器或者代理伺服器地址)。在這種情況下,連線數的計算可能涉及到不止一個終端使用者。

  • 422 從當前客戶端所在的IP地址到伺服器的連線數超過了伺服器許可的最大範圍。通常,這裡的IP地址指的是從伺服器上看到的客戶端地址(比如使用者的閘道器或者代理伺服器地址)。在這種情況下,連線數的計算可能涉及到不止一個終端使用者。 422 請求格式正確,但是由於含有語義錯誤,無法響應。(RFC 4918 WebDAV)423 Locked   當前資源被鎖定。(RFC 4918 WebDAV) 424 由於之前的某個請求發生的錯誤,導致當前請求失敗,例如 PROPPATCH。(RFC 4918 WebDAV)

  • 425 在WebDav Advanced Collections 草案中定義,但是未出現在《WebDAV 順序集協議》(RFC 3658)中。

  • 426 客戶端應當切換到TLS/1.0。(RFC 2817)

  • 449 由微軟擴充套件,代表請求應當在執行完適當的操作後進行重試。

  • 500 伺服器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理。一般來說,這個問題都會在伺服器的程式碼出錯時出現。

  • 501 伺服器不支援當前請求所需要的某個功能。當伺服器無法識別請求的方法,並且無法支援其對任何資源的請求。

  • 502 作為閘道器或者代理工作的伺服器嘗試執行請求時,從上游伺服器接收到無效的響應。

  • 503 由於臨時的伺服器維護或者過載,伺服器當前無法處理請求。這個狀況是臨時的,並且將在一段時間以後恢復。如果能夠預計延遲時間,那麼響應中可以包含一個 Retry-After 頭用以標明這個延遲時間。如果沒有給出這個 Retry-After 資訊,那麼客戶端應當以處理500響應的方式處理它。   注意:503狀態碼的存在並不意味著伺服器在過載的時候必須使用它。某些伺服器只不過是希望拒絕客戶端的連線。

  • 504 作為閘道器或者代理工作的伺服器嘗試執行請求時,未能及時從上游伺服器(URI標識出的伺服器,例如HTTP、FTP、LDAP)或者輔助伺服器(例如DNS)收到響應。   注意:某些代理伺服器在DNS查詢超時時會返回400或者500錯誤

  • 505 伺服器不支援,或者拒絕支援在請求中使用的 HTTP 版本。這暗示著伺服器不能或不願使用與客戶端相同的版本。響應中應當包含一個描述了為何版本不被支援以及伺服器支援哪些協議的實體。

  • 506 由《透明內容協商協議》(RFC 2295)擴充套件,代表伺服器存在內部配置錯誤:被請求的協商變元資源被配置為在透明內容協商中使用自己,因此在一個協商處理中不是一個合適的重點。

  • 507 伺服器無法儲存完成請求所必須的內容。這個狀況被認為是臨時的。WebDAV (RFC 4918)

  • 509 伺服器達到頻寬限制。這不是一個官方的狀態碼,但是仍被廣泛使用。

  • 510 獲取資源所需要的策略並沒有沒滿足。(RFC 2774)

3.伺服器端快取之分散式快取

3.1一致性Hash演算法

在分散式場景下的Hash演算法,其典型應用場景:有N臺伺服器提供快取服務,需要對伺服器進行負載均衡,將請求平均分發到每臺伺服器上,每臺機器負責1/N的服務。

3.1.1演算法簡述

一致性雜湊演算法(Consistent Hashing Algorithm)是一種分散式演算法,常用於負載均衡。Memcached client也選擇這種演算法,解決將key-value均勻分配到眾多Memcached server上的問題。它可以取代傳統的取模操作,解決了取模操作無法應對增刪Memcached Server的問題(增刪server會導致同一個key,在get操作時分配不到資料真正儲存的server,命中率會急劇下降)。

簡單來說,一致性雜湊將整個雜湊值空間組織成一個虛擬的圓環,如假設某雜湊函式H的值空間為0 - (2^32)-1(即雜湊值是一個32位無符號整形),整個雜湊空間環如下: 在這裡插入圖片描述

整個空間按順時針方向組織。0和(2^32)-1在零點中方向重合。

下一步將各個伺服器使用H進行一個雜湊,具體可以選擇伺服器的ip或主機名作為關鍵字進行雜湊,這樣每臺機器就能確定其在雜湊環上的位置,這裡假設將上文中三臺伺服器使用ip地址雜湊後在環空間的位置如下: 在這裡插入圖片描述

接下來使用如下演算法定位資料訪問到相應伺服器:將資料key使用相同的函式H計算出雜湊值h,通根據h確定此資料在環上的位置,從此位置沿環順時針“行走”,第一臺遇到的伺服器就是其應該定位到的伺服器。

例如我們有A、B、C、D四個資料物件,經過雜湊計算後,在環空間上的位置如下: 在這裡插入圖片描述

根據一致性雜湊演算法,資料A會被定為到Server 1上,D被定為到Server 3上,而B、C分別被定為到Server 2上。

3.1.2 容錯性與可擴充套件性分析

下面分析一致性雜湊演算法的容錯性和可擴充套件性。現假設Server 3宕機了: 在這裡插入圖片描述 可以看到此時A、C、B不會受到影響,只有D節點被重定位到Server 2。一般的,在一致性雜湊演算法中,如果一臺伺服器不可用,則受影響的資料僅僅是此伺服器到其環空間中前一臺伺服器(即順著逆時針方向行走遇到的第一臺伺服器)之間資料,其它不會受到影響。

下面考慮另外一種情況,如果我們在系統中增加一臺伺服器Memcached Server 4: 在這裡插入圖片描述

此時A、D、C不受影響,只有B需要重定位到新的Server 4。一般的,在一致性雜湊演算法中,如果增加一臺伺服器,則受影響的資料僅僅是新伺服器到其環空間中前一臺伺服器(即順著逆時針方向行走遇到的第一臺伺服器)之間資料,其它不會受到影響。

綜上所述,一致性雜湊演算法對於節點的增減都只需重定位環空間中的一小部分資料,具有較好的容錯性和可擴充套件性。

3.1.3虛擬節點

一致性雜湊演算法在服務節點太少時,容易因為節點分部不均勻而造成資料傾斜問題。例如我們的系統中有兩臺伺服器,其環分佈如下: 在這裡插入圖片描述

此時必然造成大量資料集中到Server 1上,而只有極少量會定位到Server 2上。為了解決這種資料傾斜問題,一致性雜湊演算法引入了虛擬節點機制,即對每一個服務節點計算多個雜湊,每個計算結果位置都放置一個此服務節點,稱為虛擬節點。具體做法可以在伺服器ip或主機名的後面增加編號來實現。例如上面的情況,我們決定為每臺伺服器計算三個虛擬節點,於是可以分別計算“Memcached Server 1#1”、“Memcached Server 1#2”、“Memcached Server 1#3”、“Memcached Server 2#1”、“Memcached Server 2#2”、“Memcached Server 2#3”的雜湊值,於是形成六個虛擬節點: 在這裡插入圖片描述

3.1.4演算法實現(待完善)

3.2 分散式快取:Redis(待完善)

3.3 本地快取:guava cacheBuilder(待完善)

4.快取常見問題

4.1 資料一致性

    快取是在資料持久化之前的一個節點,主要是將熱點資料放到離使用者最近或訪問速度更快的介質中,加快資料的訪問,減小響應時間。     因為快取屬於持久化資料的一個副本,因此不可避免的會出現資料不一致問題。導致髒讀或讀不到資料的情況。資料不一致,一般是因為網路不穩定或節點故障導致。根據資料的操作順序,主要有以下幾種情況。

場景介紹

  1. 先寫快取,再寫資料庫     假如快取寫成功,但寫資料庫失敗或響應延遲,則下次讀取(併發讀)快取時,就出現髒讀。

場景分析:這個寫快取的方式,本身就是錯誤的,需要改為先寫持久化介質,再寫快取的方式。

  1. 先寫資料庫,再寫快取     假如寫資料庫成功,但寫快取失敗,則下次讀取(併發讀)快取時,則讀不到資料。

場景分析: (1)根據寫入快取的響應來進行判斷,如果快取寫入失敗,則回滾資料庫操作;此種方法增加了程式的複雜度,不建議採用; (2)快取使用時,假如讀快取失敗,先讀資料庫,再回寫快取的方式實現。

  1. 快取非同步重新整理     指資料庫操作和寫快取不在一個操作步驟中,比如在分散式場景下,無法做到同時寫快取或需要非同步重新整理(補救措施)時候。     此種情況,主要考慮資料寫入和快取重新整理的時效性。比如多久內重新整理快取,不影響使用者對資料的訪問。

場景分析: (1)首先確定,哪些資料適合此類場景; (2)根據經驗值確定合理的資料不一致時間,使用者資料重新整理的時間間隔。

  1. 關於一致性的其他有效的解決方案 (1)超時:設定合理的超時時間; (2)重新整理:定時重新整理一定範圍內(根據時間,版本號)的資料;

以上是簡化資料讀寫場景,實際中會分為: (1)快取與資料庫之間的一致性; (2)多級快取之前的一致性; (3)快取副本之前的一致性。

4.2 快取高可用

    業界有兩種理論,第一套快取就是快取,臨時儲存資料的,不需要高可用。第二種快取逐步演化為重要的儲存介質,需要做高可用。

    本人的看法是,快取是否高可用,需要根據實際的場景而定。臨界點是是否對後端的資料庫造成影響。

    具體的決策依據需要根據,叢集的規模(資料,快取),成本(伺服器,運維),系統性能(併發量,吞吐量,響應時間)等方面綜合評價。

    解決方法:快取的高可用,一般通過分散式和複製實現。分散式實現資料的海量快取,複製實現快取資料節點的高可用。架構圖如下:

    其中,分散式採用一致性Hash演算法,複製採用非同步複製。

其他方法 (1)複製雙寫:快取節點的複製,由非同步改為雙寫,只有兩份都寫成功,才算成功。 (2)虛擬層:一致性Hash存在,假如其中一個HASH環不可用,資料會寫入臨近的環,當HASH可用時,資料又寫入正常的HASH環,會導致資料偏移問題。這種情況,可以考慮在HASH環前面加一個虛擬層實現。 (3)多級快取:比如一級使用本地快取,二級採用分散式Cahce,三級採用分散式Cache+本地持久化; 方式很多,需要根據業務場景靈活選擇。

4.3 快取雪崩

雪崩是指當大量快取失效時,導致大量的請求訪問資料庫,導致資料庫伺服器,無法抗住請求或掛掉的情況。

解決方法: (1)合理規劃快取的失效時間; (2)合理評估資料庫的負載壓力; (3)對資料庫進行過載保護或應用層限流; (4)多級快取設計,快取高可用。

4.4 快取穿透

快取一般是Key,value方式存在,當某一個Key不存在時會查詢資料庫,假如這個Key,一直不存在,則會頻繁的請求資料庫,對資料庫造成訪問壓力。

解決方法: (1)對結果為空的資料也進行快取,當此key有資料後,清理快取; (2)一定不存在的key,採用布隆過濾器,建立一個大的Bitmap中,查詢時通過該bitmap過濾。