天天看點

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

阿裡雲 移動服務

泠茗

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

域名(Domain Name),是由一串用點分隔的名字組成的網際網路上某台計算機或某組計算機的辨別,它的目的是為了友善人們更簡單便捷地通路網際網路上的服務。在實際的系統實作中,域名通過DNS(Domain Name System)系統轉化為伺服器的IP位址,以友善機器通過IP進行尋址和通信。上述行為,我們稱之為域名解析。

作為一次網絡通信最前置的環節,域名解析的重要性不言而喻。在傳統的基于浏覽器的網站通路場景下,域名解析環節由浏覽器核心實作,網站開發者無需關心域名解析的細節。But there are always two sides to every coin,一旦域名解析環節發生異常,開發者面對這樣的黑盒架構就會顯得束手無策,一個很典型的例子即域名劫持問題,關于這一點我們在後文會有更詳細的介紹。

進入移動網際網路時代,大量的應用基于C/S架構建構。相較于傳統的面向浏覽器的Web App,C/S架構的應用賦予了我們非常大的軟體定制空間,開發者甚至可以滲透到整個應用的底層網絡實作當中,域名解析環節的優化是以變為了可能。本篇文章我們就一起來看一看傳統域名解析存在的問題,對應的根源,以及可能的優化方案。

關于域名解析,你應該知道的基本概念

在了解傳統域名解析的流程之前,有幾個專有名詞我們需要了解一下:

根域、頂級域、二級域

DNS系統一般采用樹狀結構進行組織,以

ru.wikipedia.org

為例,

org

為頂級域名,

wikipedia

為二級域名,

ru

為三級域名,域名樹狀組織結構如下圖所示。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

權威DNS

權威DNS即最終決定域名解析結果的伺服器,開發者可以在權威DNS上配置、變更、删除具體域名的對應解析結果資訊。阿裡雲雲解析(

https://wanwang.aliyun.com/domain/dns?source=5176.11533457&userCode=w1g62dpe&type=copy https://wanwang.aliyun.com/domain/dns

)即權威DNS服務提供商。

遞歸DNS

遞歸DNS又稱為Local DNS,它沒有域名解析結果的決定權,但代理了使用者向權威DNS擷取域名解析結果的過程。遞歸DNS上有緩存子產品,當目标域名存在緩存解析結果并且TTL未過期時(每個域名都有TTL時間,即有效生存時間,若域名解析結果緩存的時間超過TTL,需要重新向權威DNS擷取解析結果),遞歸DNS會傳回緩存結果,否則,遞歸DNS會一級一級地查詢各個層級域名的權威DNS直至擷取最終完整域名的解析結果。關于域名解析的具體流程下文會舉例說明。

公共DNS

公共DNS是遞歸DNS的一種特例,它是一種全網開放的遞歸DNS服務,而傳統的遞歸DNS資訊一般由營運商分發給使用者。一個比較典型的公共DNS即Google的

8.8.8.8

,我們可以通過在作業系統配置檔案中配置公共DNS來代替Local DNS完成域名解析流程。

在實際的使用過程中,我們通常不需要手工指定自己的Local DNS位址。營運商會通過DHCP協定在系統網絡初始化階段将Local DNS位址配置設定給我們的計算機。當我們需要使用公共DNS服務時,我們就必須手工指定這些服務的位址。以Linux為例,我們可以通過在'/etc/resolv.conf'中添加Local DNS位址項來改變本機Local DNS的位址。

了解了上述域名解析相關的常見術語,我們再來仔細看一看一次域名解析流程具體是如何發生的。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

如上圖所示,以通路

www.taobao.com

為例,一次完整的域名解析流程包括:

  • 終端向Local DNS發起域名解析請求;
  • Local DNS在擷取到域名解析請求後首先從Root hints擷取根域名伺服器的位址(Root hints包含了網際網路DNS根伺服器的位址資訊);
  • 擷取了根域名伺服器位址後Local DNS向根域名伺服器發起DNS解析請求,根域名伺服器傳回

    com

    頂級域名伺服器位址;
  • 随後Local DNS向

    com

    域名伺服器發起解析請求,并得到

    taobao.com

    二級域名伺服器的位址;
  • Local DNS向

    taobao.com

    二級域名伺服器發起解析請求,并最終獲得了

    www.taobao.com

    的IP位址資訊;
  • Local DNS将遞歸查詢獲得的IP位址資訊緩存并傳回給用戶端;
Local DNS伺服器包含緩存子產品,在實際域名解析過程中Local DNS伺服器會首先查詢緩存,緩存命中且解析結果TTL未過期的情況下直接傳回,否則才啟動遞歸查詢的流程。

傳統的域名解析面臨的問題

了解了域名解析的基本概念和整體流程,我們再一起來探究一下傳統域名解析存在的一系列問題。

域名劫持

域名劫持一直是困擾許多開發者的問題之一,其表現即域名A應該傳回的DNS解析結果IP1被惡意替換為了IP2,導緻A的通路失敗或通路了一個不安全的站點。下面我們一起看看幾種常見的域名劫持的場景。

一種可能的域名劫持方式即黑客侵入了寬帶路由器并對終端使用者的Local DNS進行篡改,指向黑客自己僞造的Local DNS,進而通過控制Local DNS的邏輯傳回錯誤的IP資訊進行域名劫持。另一方面,由于DNS解析主要是基于UDP協定,除了上述攻擊行為外,攻擊者還可以監聽終端使用者的域名解析請求,并在Local DNS傳回正确結果之前将僞造的DNS解析響應傳遞給終端使用者,進而控制終端使用者的域名通路行為。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

上述攻擊行為的影響面相對比較有限,另一種我們最常碰到的域名劫持現象是緩存污染。我們知道在接收到域名解析請求時,Local DNS首先會查找緩存,如果緩存命中就會直接傳回緩存結果,不再進行遞歸DNS查詢。這時候如果Local DNS針對部分域名的緩存進行更改,比如将緩存結果指向第三方的廣告頁,就會導緻使用者的通路請求被引導到這些廣告頁位址上。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

對比第一種攻擊,這類緩存污染往往能帶來更明顯的群體傷害,比如某個省份某個營運商的使用者群可能因為該地區Local DNS的緩存污染而導緻通路服務異常。這類緩存污染行為往往是間歇性、局部性發生的,沒有明顯的規律,導緻開發者很難對其進行量化、評估、預防。

有的同學可能會問,“我使用了HTTPS,是否就可以避免域名劫持的問題”,答案是否定的。域名解析環節發生在網絡加密請求互動之前,試想一下,如果用戶端還沒有服務端的确切位址資訊,我們又如何知道應該和誰進行加密的握手協商與通信呢?

排程不精準

除了域名劫持問題,基于傳統Local DNS的域名解析還會帶來域名排程精準性的問題。對于類似CDN域名通路這類需要按地域、營運商進行智能解析排程的場景,精準排程的訴求是十分強烈的。

關于排程不精準的原因,我們主要可以從兩個方面來探究一下。第一個常見的問題即解析轉發。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

部分Local DNS供應商為了降低營運成本,會将請求到自己節點的域名解析請求轉發給其他供應商的Local DNS節點,如上圖所示。假如使用者請求解析一個CDN域名

cdn.aliyun.com

,使用者配置設定到的Local DNS A為了節省成本,把該次請求轉發給了另一營運商的Local DNS B,權威DNS在進行域名解析時會根據Local DNS的IP資訊進行智能排程,即權威DNS會根據Local DNS B的IP

78.29.29.1

進行排程,配置設定與

78.29.29.1

相同營運商并且地理位置最近的CDN節點

78.29.29.2

,然而這個CDN節點對于終端

135.35.35.1

而言并不是最優的CDN節點,他們分屬不同的營運商,并且地理位置上可能相隔很遠。這類解析轉發行為會嚴重影響域名解析的精準性并對使用者業務通路延遲帶來影響。

除了解析轉發對排程精準性帶來的影響外,Local DNS的布署情況同樣影響着域名智能解析的精準性。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

如上圖所示,部分營運商Local DNS的布點受成本因素制約分布并不均勻,比如在東部地區部署比較密集,但在西部地區部署比較稀疏。這時候當一位西藏的使用者準備通路CDN節點時,我們預期他應該會被排程到西藏的CDN節點A上以實作就近接入和通路加速。但由于Local DNS的資源有限,西部地區的終端使用者被統一排程到青海的Local DNS B上,這時候權威DNS根據Local DNS B的IP進行CDN域名的智能解析,并将青海的CDN節點B傳回給西藏使用者,導緻使用者的網絡通路延遲上升。另一種我們實際發現的情況是Local DNS的配置設定甚至并非遵循就近原則,比如有實際案例顯示西藏的使用者甚至被配置設定了北京的Local DNS節點C,導緻西藏的使用者在進行CDN資源通路時被排程到了北京的CDN節點C上,類似的由于排程精度的缺失帶來的通路體驗的影響是非常嚴重的。

解析生效滞後

部分業務場景下開發者對域名解析結果變更的生效時間非常敏感(這部分變更操作是開發者在權威DNS上完成的),比如當業務伺服器受到攻擊時,我們需要最快速地将業務IP切換到另一組叢集上,這樣的訴求在傳統域名解析體系下是無法完成的。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

Local DNS的部署是由各個地區的各個營運商獨立部署的,是以各個Local DNS的服務品質參差不齊。在對域名解析緩存的處理上,各個獨立節點的實作政策也有差別,比如部分節點為了節省開支忽略了域名解析結果的TTL時間限制,導緻使用者在權威DNS變更的解析結果全網生效的周期非常漫長(我們已知的最長生效時間甚至高達48小時)。這類延遲生效可能直接導緻使用者業務通路的異常。

延遲大

DNS首次查詢或緩存過期後的查詢,需要遞歸周遊多個DNS伺服器以擷取最終的解析結果,這增加了網絡請求的前置延時時間。特别是在移動網際網路場景下,移動網絡品質參差不齊,弱網環境的RTT時間可能高達數百毫秒,對于一次普通的業務請求而言,上述延時是非常沉重的負擔。另一方面,弱網環境下的解析逾時、解析失敗等現象屢見不鮮,如何合理優化DNS解析對于整體網絡通路品質的提升至關重要。

HTTPDNS

通過上文的介紹,聰明的讀者應該可以發現,傳統域名解析面臨的諸多問題與挑戰本質根源在于Local DNS的服務品質不可控,如果有一個更安全、穩定、高效的遞歸DNS服務幫助我們代理了域名解析的過程,上述問題看起來就可以徹底地得到解決。

HTTPDNS在這樣的背景下應運而生。我們一起來看看HTTPDNS的基本概念以及它是如何解決傳統DNS解析面臨的問題的。

防域名劫持

HTTPDNS使用HTTP協定進行域名解析,代替現有基于UDP的DNS協定,域名解析請求直接發送到HTTPDNS服務端,進而繞過營運商的Local DNS,如下圖所示。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

HTTPDNS代替了傳統的LocalDNS完成遞歸解析的功能,基于HTTP協定的設計可以适用于幾乎所有的網絡環境,同時保留了鑒權、HTTPS等更高安全性的擴充能力,避免惡意攻擊劫持行為。另一方面,商業化的HTTPDNS服務(

https://www.aliyun.com/product/httpdns?source=5176.11533457&userCode=w1g62dpe&type=copy https://www.aliyun.com/product/httpdns

)緩存管理有嚴格的SLA保障,避免了類似Local DNS的緩存污染的問題。

精準排程

傳統域名解析的排程精準性問題,本質根源在于Local DNS的部署和配置設定機制上。由于碎片化的管理方式,這些環節的服務品質同樣很難得到保障。HTTPDNS在遞歸解析實作上優化了與權威DNS的互動,通過edns-client-subnet協定(

https://datatracker.ietf.org/doc/rfc7871 https://datatracker.ietf.org/doc/rfc7871

)将終端使用者的IP資訊直接傳遞給權威DNS,這樣權威DNS就可以忽略Local DNS IP資訊,根據終端使用者的IP資訊進行精準排程,避免Local DNS的坐标幹擾(當然上述精準排程方案的前提是權威DNS需要支援edns-client-subnet,可喜的是目前主流的權威DNS服務都已支援該協定)。精準排程的流程示例如下。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

實時生效

在域名解析生效周期方面,HTTPDNS也有着傳統域名解析體系所無法具備的能力。前文中我們提到由于各個地區的Local DNS是獨立維護的,服務品質參差不齊,緩存實作不一,是以導緻的解析變更全網生效滞後的問題,在商業化的HTTPDNS服務上就不會存在(HTTPDNS嚴格遵循DNS TTL限制進行緩存更新)。另一方面,即便我們假設Local DNS嚴格遵循域名TTL時間進行緩存管理(這裡我們假設開發者配置的域名TTL時間為5min),當開發者業務受到攻擊并需要快速進行切換時,Local DNS也會遵循域名TTL,在持續5min的時間段内傳回舊IP資訊,這5min的業務影響對于中大型企業而言是一個不小的損失(對于電商類的大型企業,5min的通路異常可能意味着幾百萬的交易額下跌)。以阿裡雲HTTPDNS服務(

https://www.aliyun.com/product/httpdns?source=5176.11533457&userCode=w1g62dpe&type=copy

)為例,HTTPDNS在快速生效方面有專有的方案,配合阿裡雲的權威DNS服務雲解析(

https://wanwang.aliyun.com/domain/dns?source=5176.11533457&userCode=w1g62dpe&type=copy

),使用者在權威DNS變更的解析結果将快速同步給HTTPDNS,覆寫原有的緩存記錄,幫助使用者實作秒級的域名解析切換。

在DNS解析延遲方面,由于HTTPDNS基于HTTP協定,而HTTP基于TCP協定,對比傳統的UDP傳輸多了一些備援的握手環節,是以從原理上而言網絡請求方面的開銷并沒有降低。但在實際使用過程中,我們可以通過端上的政策來實作一個零延遲DNS解析的方案。接下來我們一起來看看HTTPDNS服務在移動端的最佳實踐方案。實時生效的流程如下圖所示。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

域名解析最佳實踐

通過HTTPDNS服務,我們可以實作包括防止域名劫持、精準排程、實時解析生效等功能,但在DNS解析開銷的優化上,我們需要用戶端一起配合。

預解析

絕大多數的APP在應用初始化階段都有一個啟動期,我們可以在這個啟動期做一些preflight工作,即在初始化階段我們可以針對業務的熱點域名在背景發起異步的HTTPDNS解析請求。這部分預解析結果在後續的業務請求中可以直接使用,進而消除首次業務請求的DNS解析開銷,提升APP首頁的加載速度。

在用戶端實際使用HTTPDNS的過程中,有一個大家需要關注的點。标準的Web伺服器(以Nginx為例)一般會将HTTP請求頭中的Host頭的值作為請求的域名資訊進行處理(取決于服務端的配置,但一般情況都如此)。比如當我們通過标準的網絡庫通路

www.aliyun.com/index.html

這個位址時,發出的網絡請求一般是這樣的:

> GET /index.html HTTP/1.1
> Host: www.aliyun.com
> User-Agent: curl/7.43.0
> Accept: /           

使用HTTPDNS後,我們需要将HTTP請求URL中的Host域(注意這裡的Host域指的是URL中的Host字段,而非HTTP請求頭中的Host頭)替換為HTTPDNS解析獲得的IP,這時由于标準的網絡庫會将URL中的Host域指派給HTTP請求頭中的Host頭,發出的網絡請求如下:

> GET /index.html HTTP/1.1
> Host: 140.205.63.8
> User-Agent: curl/7.43.0
> Accept: /           

上述Host資訊将導緻服務端的解析異常(服務端配置的是域名資訊,而非IP資訊,試想一下如果我們的服務端服務了兩個域名

www.a.com

www.b.com

,這時候它接收到一個

140.205.63.8/index.html

請求,它如何判斷應該傳回a的首頁還是b的首頁資訊呢?)。為了解決這個問題,我們需要主動設定HTTP請求Host頭的值,以Android的官方網絡庫

HttpURLConnection

為例:

String originalUrl = “        http://www.aliyun.com/index.htm               l";
URL url = new URL(originalURL);
String originalHost = url.getHost();
// 同步擷取IP
String ip = httpdns.getIpByHost(originalHost);
HttpURLConnection conn;
if (ip != null) {           
// 通過HTTPDNS擷取IP成功,進行URL替換和Host頭設定
url = new URL(originalUrl.replaceFirst(originalHost, ip));
conn = (HttpURLConnection) url.openConnection();
// 設定請求Host頭
conn.setRequestProperty("Host", originHost);                

} else {

conn = (HttpURLConnection) url.openConnection();                

}

主動設定Host頭後,發出的網絡請求就與未替換URL的網絡請求一模一樣了。

智能緩存

通過預解析擷取的IP有一定的TTL有效時間,我們需要合理地緩存下來進行管理。作業系統本身的DNS緩存粒度比較粗,在用戶端我們可以應用更細粒度的緩存管理來提升解析效率。比如在不同的網絡營運商環境下,對CDN域名的解析結果會發生變化,當我們使用電信WIFI時,DNS解析會傳回就近的電信CDN節點IP,當我們使用聯通3G時,DNS解析會傳回就近的聯通CDN節點IP,針對不同營運商的解析結果緩存可以確定我們在網絡切換時能夠快速地進行網絡請求,減免DNS解析帶來的額外開銷。甚至更激進的,我們可以做本地的持久化緩存,當下一次APP啟動時直接讀取緩存用于網絡通路,以提升首屏加載的速度。

懶加載

懶加載政策的實施可以讓我們真正實作DNS的零延遲解析。所謂懶加載政策,核心的實作思路如下:

  • 業務層的域名解析請求隻和緩存進行互動,不實際發生網絡解析請求。如果緩存中存在記錄,不論過期與否,直接傳回業務層緩存中的記錄;
  • 如果緩存中的記錄已過期,背景發起異步網絡請求進行HTTPDNS解析;

有的同學可能會有疑惑,傳回一個過期的IP豈不是違背了TTL設計的初衷?的确,上述行為并不符合标準的規範,但是當我們重新審視一下自己的業務特點,上述的變通政策就顯得非常有意義了。絕大多數的業務場景下我們的後端IP是固定的若幹個節點,是以連續的解析結果在環境不變的情況下有很大機率是保持一緻的,這在一定程度上保證了懶加載的可行性。另一方面,即便我們由于傳回過期IP導緻了通路異常的行為,背景很快會進行新IP的異步解析和緩存更新,業務本身可以進行重試和快速的複原,是以上述行為帶來的影響也是非常小的。再進一步,TTL過期的IP的服務在絕大多數場景下還是持續的,可預期的,是以懶加載可能帶來的業務風險是完全可控的。通過0.1%場景下的業務瞬時通路風險來換取99.9%場景下的使用者體驗提升,這筆買賣還是非常劃算的(當然懶加載的使用有賴于合适的業務場景,如果你的業務場景下IP變化頻繁,并且TTL過期的IP通路不可用,是不建議應用懶加載政策的)。

下圖描繪了預解析+懶加載的實作架構:

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

綜上可以看到,當我們需要實作零延遲解析的效果時,在用戶端還是有比較多的工作需要做的。商業化的HTTPDNS服務(

https://wanwang.aliyun.com/domain/dns?source=5176.11533457&userCode=w1g62dpe&type=copy

)提供了終端SDK友善開發者進行終端上的內建和使用,推薦大家可以嘗試一下。

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

ru.wikipedia.org

org

wikipedia

ru

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇
https://wanwang.aliyun.com/domain/dns?source=5176.11533457&userCode=w1g62dpe&type=copy

8.8.8.8

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

www.taobao.com

  • com

  • com

    taobao.com

  • taobao.com

    www.taobao.com

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇
移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

cdn.aliyun.com

78.29.29.1

78.29.29.1

78.29.29.2

135.35.35.1

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇
https://www.aliyun.com/product/httpdns?source=5176.11533457&userCode=w1g62dpe&type=copy

https://datatracker.ietf.org/doc/rfc7871
移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

https://www.aliyun.com/product/httpdns?source=5176.11533457&userCode=w1g62dpe&type=copy https://wanwang.aliyun.com/domain/dns?source=5176.11533457&userCode=w1g62dpe&type=copy
移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇

www.aliyun.com/index.html

> GET /index.html HTTP/1.1
> Host: www.aliyun.com
> User-Agent: curl/7.43.0
> Accept: /           
> GET /index.html HTTP/1.1
> Host: 140.205.63.8
> User-Agent: curl/7.43.0
> Accept: /           

www.a.com

www.b.com

140.205.63.8/index.html

HttpURLConnection

String originalUrl = “        http://www.aliyun.com/index.htm               l";
URL url = new URL(originalURL);
String originalHost = url.getHost();
// 同步擷取IP
String ip = httpdns.getIpByHost(originalHost);
HttpURLConnection conn;
if (ip != null) {           
// 通過HTTPDNS擷取IP成功,進行URL替換和Host頭設定
url = new URL(originalUrl.replaceFirst(originalHost, ip));
conn = (HttpURLConnection) url.openConnection();
// 設定請求Host頭
conn.setRequestProperty("Host", originHost);                

} else {

conn = (HttpURLConnection) url.openConnection();                

}

移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇移動網際網路時代 , 如何優化你的網絡 —— 域名解析篇
https://wanwang.aliyun.com/domain/dns?source=5176.11533457&userCode=w1g62dpe&type=copy

繼續閱讀