構建完善微服務——API網關

數據智能相依偎 2024-04-23 07:44:39

系統拆分爲微服務後,內部的微服務之間是互聯互通的,相互之間的訪問都是點對點的。如果外部系統想調用系統的某個功能,也采取點對點的方式,則外部系統會非常“頭大”。因爲在外部系統看來,它不需要也沒辦法理解這麽多微服務的職責分工和邊界,它只會關注它需要的能力,而不會關注這個能力應該由哪個微服務提供。

存在跨域請求,在一定場景下處理相對複雜認證複雜,每個服務都需要獨立認證難以重構,隨著項目的叠代,可能需要重新劃分微服務。例如,可能將多個服務合並成一個或者將一個服務拆分成多個。如果客戶端直接與微服務通信,那麽重構將會很難實施某些微服務可能使用了防火牆 / 浏覽器不友好的協議,直接訪問會有一定的困難

1.網關功能

通過責任鏈模式,基于 Filter 鏈的方式提供了網關基本的功能,例如:統一入口 、鑒權、路由、協議轉換、緩存、限流、監控、日志。也可以根據實際的業務需要進行擴展,但注意不要做耗時操作。

(1)入口網關包括內容

綜合上面的分析,微服務需要一個統一的 API 網關,負責外部系統的訪問操作。API 網關是外部系統訪問的接口,所有的外部系統接⼊系統都需要通過 API 網關,主要包括:

①路由轉發

「API網關」是內部微服務的對外唯一入口,所以外面全部的請求都會先發到這個「API網關」上,然後由「API網關」來根據不同的請求去路由到不同的微服務節點上。例如可以 根據路徑 來轉發、也可以 根據參數 來轉發。

並且由于內部微服務實例也會隨著業務調整不停的變更,增加或者刪除節點,「API網關」可以與「服務注冊」模塊進行協同工作,保證將外部請求轉發到最合適的微服務實例上面去。

服務路由和服務發現緊密相關,服務路由一般不會設計成一個獨立運行的系統,通常情況下是和服務發現放在一起實現的。對于自理式服務發現,服務路由是微服務內部實現的;對于代理式服務發現,服務路由是由 LOAD BALANCER 系統實現的。無論放在哪裏實現,服務路由核心的功能就是路由算法。常見的路由算法有:隨機路由、輪詢路由、最小壓力路由、最小連接數路由等。

負載均衡、本地路由優先等路由策略可以滿足大部分場景,但是一些場景需要對路由策略設置一些過濾條件。比較常用的是:

條件路由規則黑白名單流量引導,只暴漏部分服務提供者,將異常峰值流量導流到幾台服務器上,防止生産系統雪崩讀寫分離前後台分離灰度升級,將應用路由到新的服務版本,按照手機號,設備,ip等規則路由到不同的集群環境腳本路由規則。

②負載均衡/限流

既然「API網關」是內部微服務的單一入口,所以「API網關」在收到外部請求之後,還可以根據內部微服務每個實例的負荷情況進行動態的負載均衡調節。一旦內部的某個微服務實例負載很高,甚至是不能及時響應,則「API網關」就通過負載均衡策略減少或停止向這個實例轉發請求。當所有的內部微服務實例都處理不過來的時候,「API網關」還可以采用限流或熔斷的形式阻止外部請求,以保障整個系統的可用性。

③安全認證

「API網關」就像是微服務的大門守衛,每一個請求進來之後,都必須先在「API網關」上進行身份驗證,身份驗證通過後才轉發給後面的服務,轉發的時候一般也會帶上身份信息。同時「API網關」也需要對每一個請求進行安全性檢查,例如參數的安全性、傳輸的安全性等等。

④日志記錄

既然所有的請求都需要走「API網關」,那麽我們就可以在「API網關」上統一集中的記錄下這些行爲日志。這些日志既可以作爲我們後續事件查詢使用,也可以作爲系統的性能監控使用。

⑤數據轉換

因爲「API網關」對外是面向多種不同的客戶端,不同的客戶端所傳輸的數據類型可能是不一樣的。因此「API網關」還需要具備數據轉換的功能,將不同客戶端傳輸進來的數據轉換成同一種類型再轉發給內部微服務上,這樣,兼容了這些請求的多樣性,保證了微服務的靈活性。

⑥黑白名單相關

比如針對設備 ID、用戶 IP、用戶 ID 等維度的黑白名單

⑦服務注冊

爲了能夠代理後面的服務,並把請求路由到正確的位置上,網關應該有服務注冊功能,也就是後端的服務實例可以把其提供服務的地址注冊、取消注冊。一般來說,注冊也就是注冊一些 API 接口。比如,HTTP 的 Restful 請求,可以注冊相應 API 的 URI、方法、HTTP 頭。這樣,Gateway 就可以根據接收到的請求中的信息來決定路由到哪一個後端的服務上。

⑧彈力設計

網關還可以把彈力設計中的那些異步、重試、冪等、流控、熔斷、監視等都可以實現進去。這樣,同樣可以像 Service Mesh 那樣,讓應用服務只關心自己的業務邏輯(或是說數據面上的事)而不是控制邏輯(控制面)。

(2)出口網關

在系統開發中,會依賴很多外部的第三方系 統,比如典型的例子:第三方賬戶登錄、使用第三方工具支付等等。可以在應用服務器 和第三方系統之間,部署出口網關,在出口網關中,對調用外部的 API 做統一的認證、授 權,審計以及訪問控制。

2.網關優點

安全 ,只有網關系統對外進行暴露,微服務可以隱藏在內網,通過防火牆保護。易于監控。可以在網關收集監控數據並將其推送到外部系統進行分析。易于認證。可以在網關上進行認證,然後再將請求轉發到後端的微服務,而無須在每個微服務中進行認證。減少了客戶端與各個微服務之間的交互次數易于統一授權。

3.網關關注點

(1)設計重點

性能,API 入口網關承擔從 客戶端的所有流量。假如業務服務處理時間是 10ms,而 API 網關的耗時在 1ms,那麽相當于每個接口的響應時間都要增加 10%,這對于性能的影響無疑是巨大的。而提升 API 網 關性能的關鍵還是在 I/O 模型上。擴展性,也就是可以隨時在網關的執行鏈路上,增加一些邏輯,也可以隨時下掉一些邏輯(也就是所謂的熱插拔)。 所以一般來說,可以把每一個操作定義爲一個 filter(過濾器),然後使用“責任鏈模 式”將這些 filter 串起來。責任鏈可以動態地組織這些 filter,解耦 filter 之間的關系,無論是增加還是減少 filter,都不會對其他的 filter 有任何的影響。Zuul 就是采用責任鏈模式,Zuul1 中將 filter 定義爲三類:pre routing filter(路由前過 濾器)、routing filter(路由過濾器)和 after routing filter(路由後過濾器)。每一個 filter 定義了執行的順序,在 filter 注冊時,會按照順序插入到 filter chain(過濾器鏈) 中。這樣 Zuul 在接收到請求時,就會按照順序依次執行插入到 filter chain 中的 filter 了。業務松耦合,協議緊耦合 。在業務設計上,網關不應與後面的服務之間形成服務耦合,也不應該有業務邏輯。網關應該是在網絡應用層上的組件,不應該處理通訊協議體,只應該解析和處理通訊協議頭。另外,除了服務發現外,網關不應該有第三方服務的依賴。應用監視,提供分析數據 。網關上需要考慮應用性能的監控,除了有相應後端服務的高可用的統計之外,還需要使用 Tracing ID 實施分布式鏈路跟蹤,並統計好一定時間內每個 API 的吞吐量、響應時間和返回碼,以便啓動彈力設計中的相應策略。用彈力設計保護後端服務 。網關上一定要實現熔斷、限流、重試和超時等彈力設計。如果一個或多個服務調用花費的時間過長,那麽可接受超時並返回一部分數據,或是返回一個網關裏的緩存的上一次成功請求的數據。你可以考慮一下這樣的設計。DevOps 。因爲網關這個組件太關鍵了,所以需要 DevOps 這樣的東西,將其發生故障的概率降到最低。這個軟件需要經過精良的測試,包括功能和性能的測試,還有浸泡測試。還需要有一系列自動化運維的管控工具。可用性,因爲所有的流量或調用經過網關,所以網關必須成爲一個高可用的技術組件,它的穩定直接關系到了所有服務的穩定。網關如果沒有設計,就會成變一個單點故障。因此,一個好的網關至少要做到以下幾點。集群化 。網關要成爲一個集群,其最好可以自己組成一個集群,並可以自己同步集群數據,而不需要依賴于一個第三方系統來同步數據服務化 。網關還需要做到在不間斷的情況下修改配置,一種是像 Nginx reload 配置那樣,可以做到不停服務,另一種是最好做到服務化。也就是說,得要有自己的 Admin API 來在運行時修改自己的配置。持續化 。比如重啓,就是像 Nginx 那樣優雅地重啓。有一個主管請求分發的主進程。當我們需要重啓時,新的請求被分配到新的進程中,而老的進程處理完正在處理的請求後就退出。

(2)注意事項

不要在網關中的代碼裏內置聚合後端服務的功能,而應考慮將聚合服務放在網關核心代碼之外。可以使用 Plugin 的方式,也可以放在網關後面形成一個 Serverless 服務。網關應該靠近後端服務,並和後端服務使用同一個內網,這樣可以保證網關和後端服務調用的低延遲,並可以減少很多網絡上的問題。這裏多說一句,網關處理的靜態內容應該靠近用戶(應該放到 CDN 上),而網關和此時的動態服務應該靠近後端服務。網關也需要做容量擴展,所以需要成爲一個集群來分擔前端帶來的流量。這一點,要麽通過 DNS 輪詢的方式實現,要麽通過 CDN 來做流量調度,或者通過更爲底層的性能更高的負載均衡設備。對于服務發現,可以做一個時間不長的緩存,這樣不需要每次請求都去查一下相關的服務所在的地方。當然,如果你的系統不複雜,可以考慮把服務發現的功能直接集成進網關中。爲網關考慮 bulkhead 設計方式。用不同的網關服務不同的後端服務,或是用不同的網關服務前端不同的客戶。

另外,因爲網關是爲用戶請求和後端服務的橋接裝置,所以需要考慮一些安全方面的事宜。具體如下:

加密數據 。可以把 SSL 相關的證書放到網關上,由網關做統一的 SSL 傳輸管理。校驗用戶的請求 。一些基本的用戶驗證可以放在網關上來做,比如用戶是否已登錄,用戶請求中的 token 是否合法等。但是,我們需要權衡一下,網關是否需要校驗用戶的輸入。因爲這樣一來,網關就需要從只關心協議頭,到需要關心協議體。而協議體中的東西一方面不像協議頭是標准的,另一方面解析協議體還要耗費大量的運行時間,從而降低網關的性能。對此,我想說的是,看具體需求,一方面如果協議體是標准的,那麽可以幹;另一方面,對于解析協議所帶來的性能問題,需要做相應的隔離。檢測異常訪問 。網關需要檢測一些異常訪問,比如,在一段比較短的時間內請求次數超過一定數值;還比如,同一客戶端的 4xx 請求出錯率太高……對于這樣的一些請求訪問,網關一方面要把這樣的請求屏蔽掉,另一方面需要發出警告,有可能會是一些比較重大的安全問題,如被黑客攻擊。

4.網關分類

(1)流量網關

對于一個服務集群,勢必有很多非法的請求或者無效的請求,這時候要將請求拒之門外,降低集群的流量壓力。

定義全局性的、跟具體的後端業務應用和服務完全無關的策略網關就是上圖所示的架構模型——流量網關。流量網關通常只專注于全局的Api管理策略,比如全局流量監控、日志記錄、全局限流、黑白名單控制、接入請求到業務系統的負載均衡等,有點類似防火牆。Kong 就是典型的流量網關。業務網關一般部署在流量網關之後、業務系統之前,比流量網關更靠近業務系統。通常API網指的是業務網關。有時候我們也會模糊流量網關和業務網關,讓一個網關承擔所有的工作,所以這兩者之間並沒有嚴格的界線。

(2)業務網關

當一個單體應用被拆分成許許多多的微服務應用後,也帶來了一些問題。一些與業務非強相關的功能,比如權限控制、日志輸出、數據加密、熔斷限流等,每個微服務應用都需要,因此存在著大量重複的代碼實現。而且由于系統的叠代、人員的更替,各個微服務中這些功能的實現細節出現了較大的差異,導致維護成本變高。另一方面,原先單體應用下非常容易做的接口管理,在服務拆分後沒有了一個集中管理的地方,無法統計已存在哪些接口、接口定義是什麽、運行狀態如何。

網關就是爲了解決上述問題。作爲微服務體系中的核心基礎設施,一般需要具備接口管理、協議適配、熔斷限流、安全防護等功能,各種開源的網關産品(比如 zuul)都提供了優秀高可擴展性的架構、可以很方便的實現我們需要的一些功能、比如鑒權、日志監控、熔斷限流等。與流量網關相對應的就是業務網關,業務網關更靠近我們的業務,也就是與服務器應用層打交道,那麽有很多應用層需要考慮的事情就可以依托業務網關,例如在線程模型、協議適配、熔斷限流,服務編排等。下面看看業務網關體系結構:

目前業務網關比較成熟的 API 網關框架産品有三個 分別是:Zuul1、Zuul2 和 SpringCloud Gateway, 後面再進行對比。

5.常見網關對比

目前常見的開源網關大致上按照語言分類有如下幾類:

Nginx+lua :OpenResty、Kong、Orange、Abtesting gateway 等Java :Zuul/Zuul2、Spring Cloud Gateway、Kaazing KWG、gravitee、Dromara soul 等Go :Janus、fagongzi、Grpc-gatewayDotnet :OcelotNodeJS :Express Gateway、Micro Gateway

按照使用數量、成熟度等來劃分,主流的有 5個:

OpenRestyKongZuul、Zuul2Spring Cloud Gateway

(1)OpenResty

OpenResty是一個流量網關,OpenResty基于 Nginx與 Lua 的高性能 Web 平台,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用于方便地搭建能夠處理超高並發、擴展性極高的動態 Web 應用、Web 服務和動態網關。

通過揉和衆多設計良好的 Nginx 模塊,OpenResty 有效地把 Nginx 服務器轉變爲一個強大的 Web 應用服務器,基于它開發人員可以使用 Lua 編程語言對 Nginx 核心以及現有的各種 Nginx C 模塊進行腳本編程,構建出可以處理一萬以上並發請求的極端高性能的 Web 應用

OpenResty 最早是順應 OpenAPI 的潮流做的,所以 Open 取自“開放”之意,而Resty便是 REST 風格的意思。雖然後來也可以基于 ngx_openresty 實現任何形式的 web service 或者傳統的 web 應用。

也就是說 Nginx 不再是一個簡單的靜態網頁服務器,也不再是一個簡單的反向代理了。第二代的 openresty 致力于通過一系列 nginx 模塊,把nginx擴展爲全功能的 web 應用服務器。

ngx_openresty 是用戶驅動的項目,後來也有不少國內用戶的參與,從 openresty.org 的點擊量分布上看,國內和國外的點擊量基本持平。

ngx_openresty 目前有兩大應用目標:

通用目的的 web 應用服務器。在這個目標下,現有的 web 應用技術都可以算是和 OpenResty 或多或少有些類似,比如 Nodejs, PHP 等等。ngx_openresty 的性能(包括內存使用和 CPU 效率)算是最大的賣點之一。Nginx 的腳本擴展編程,用于構建靈活的 Web 應用網關和 Web 應用防火牆。有些類似的是 NetScaler。其優勢在于 Lua 編程帶來的巨大靈活性。

(2)Kong_性能最高

Kong基于OpenResty開發,也是流量層網關, 是一個雲原生、快速、可擴展、分布式的Api 網關。繼承了OpenResty的高性能、易擴展性等特點。Kong通過簡單的增加機器節點,可以很容易的水平擴展。同時功能插件化,可通過插件來擴展其能力。而且在任何基礎架構上都可以運行。具有以下特性:

提供了多樣化的認證層來保護Api。可對出入流量進行管制。提供了可視化的流量檢查、監視分析Api。能夠及時的轉換請求和相應。提供log解決方案可通過api調用Serverless 函數。

Kong解決了什麽問題

當我們決定對應用進行微服務改造時,應用客戶端如何與微服務交互的問題也隨之而來,畢竟服務數量的增加會直接導致部署授權、負載均衡、通信管理、分析和改變的難度增加。

面對以上問題,API GATEWAY是一個不錯的解決方案,其所提供的訪問限制、安全、流量控制、分析監控、日志、請求轉發、合成和協議轉換功能,可以解放開發者去把精力集中在具體邏輯的代碼,而不是把時間花費在考慮如何解決應用和其他微服務鏈接的問題上。

(3)Zuul1.0

Zuul是所有從設備和web站點到Netflix流媒體應用程序後端請求的前門。作爲一個邊緣服務應用程序,Zuul被構建來支持動態路由、監視、彈性和安全性。它還可以根據需要將請求路由到多個Amazon自動伸縮組。Zuul使用了一系列不同類型的過濾器,使我們能夠快速靈活地將功能應用到服務中。

過濾器

過濾器是Zuul的核心功能。它們負責應用程序的業務邏輯,可以執行各種任務。

Type :通常定義過濾器應用在哪個階段Async :定義過濾器是同步還是異步Execution Order :執行順序Criteria :過濾器執行的條件Action :如果條件滿足,過濾器執行的動作

Zuul提供了一個動態讀取、編譯和運行這些過濾器的框架。過濾器之間不直接通信,而是通過每個請求特有的RequestContext共享狀態。

下面是Zuul的一些過濾器:

Incoming.Incoming過濾器在請求被代理到Origin之前執行。這通常是執行大部分業務邏輯的地方。例如:認證、動態路由、速率限制、DDoS保護、指標。Endpoint.Endpoint過濾器負責基于incoming過濾器的執行來處理請求。Zuul有一個內置的過濾器(ProxyEndpoint),用于將請求代理到後端服務器,因此這些過濾器的典型用途是用于靜態端點。例如:健康檢查響應,靜態錯誤響應,404響應。Outgoing.Outgoing過濾器在從後端接收到響應以後執行處理操作。通常情況下,它們更多地用于形成響應和添加指標,而不是用于任何繁重的工作。例如:存儲統計信息、添加/剝離標准標題、向實時流發送事件、gziping響應。

過濾器類型

下面是與一個請求典型的生命周期對應的標准的過濾器類型:

PRE :路由到Origin之前執行ROUTING :路由到Origin期間執行POST :請求被路由到Origin之後執行ERROR :發生錯誤的時候執行

這些過濾器幫助我們執行以下功能:

身份驗證和安全性 :識別每個資源的身份驗證需求,並拒絕不滿足它們的請求監控 :在邊緣跟蹤有意義的數據和統計數據,以便給我們一個准確的生産視圖動態路由 :動態路由請求到不同的後端集群壓力測試 :逐漸增加集群的流量,以評估性能限流 :爲每種請求類型分配容量,並丟棄超過限制的請求靜態響應處理 :直接在邊緣構建一些響應,而不是將它們轉發到內部集群

Zuul 1.0 請求生命周期

Netflix宣布了通用API網關Zuul的架構轉型。Zuul原本采用同步阻塞架構,轉型後叫作Zuul2,采用異步非阻塞架構。Zuul2和Zuul1在架構方面的主要區別在于,Zuul2運行在異步非阻塞的框架上,比如Netty。Zuul1依賴多線程來支持吞吐量的增長,而Zuul 2使用的Netty框架依賴事件循環和回調函數。

(4)Zuul2.0

上圖是Zuul2的架構,和Zuul1沒有本質區別,兩點變化:

前端用Netty Server代替Servlet,目的是支持前端異步。後端用Netty Client代替Http Client,目的是支持後端異步。過濾器換了一下名字,用Inbound Filters代替Pre-routing Filters,用Endpoint Filter代替Routing Filter,用Outbound Filters代替Post-routing Filters。

Inbound Filters :路由到 Origin 之前執行,可以用于身份驗證、路由和裝飾請求

Endpoint Filters :可用于返回靜態響應,否則內置的ProxyEndpoint過濾器將請求路由到Origin

Outbound Filters :從Origin那裏獲取響應後執行,可以用于度量、裝飾用戶的響應或添加自定義header

有兩種類型的過濾器:sync 和 async。因爲Zuul是運行在一個事件循環之上的,因此從來不要在過濾中阻塞。如果你非要阻塞,可以在一個異步過濾器中這樣做,並且在一個單獨的線程池上運行,否則可以使用同步過濾器。

上文提到過Zuul2開始采用了異步模型

優勢 是異步非阻塞模式啓動的線程很少,基本上一個CPU core上只需啓一個事件環處理線程,它使用的線程資源就很少,上下文切換(Context Switch)開銷也少。非阻塞模式可以接受的連接數大大增加,可以簡單理解爲請求來了只需要進隊列,這個隊列的容量可以設得很大,只要不超時,隊列中的請求都會被依次處理。

不足 ,異步模式讓編程模型變得複雜。一方面Zuul2本身的代碼要比Zuul1複雜很多,Zuul1的代碼比較容易看懂,Zuul2的代碼看起來就比較費勁。另一方面異步模型沒有一個明確清晰的請求->處理->響應執行流程(call flow),它的流程是通過事件觸發的,請求處理的流程隨時可能被切換斷開,內部實現要通過一些關聯id機制才能把整個執行流再串聯起來,這就給開發調試運維引入了很多複雜性,比如你在IDE裏頭調試異步請求流就非常困難。另外ThreadLocal機制在這種異步模式下就不能簡單工作,因爲只有一個事件環線程,不是每個請求一個線程,也就沒有線程局部的概念,所以對于CAT這種依賴于ThreadLocal才能工作的監控工具,調用鏈埋點就不好搞(實際可以工作但需要進行特殊處理)。

總體上,異步非阻塞模式比較適用于IO密集型(IO bound)場景,這種場景下系統大部分時間在處理IO,CPU計算比較輕,少量事件環線程就能處理。

Netflix給出了一個比較模糊的數據,大致Zuul2的性能比Zuul1好20%左右 ,這裏的性能主要指每節點每秒處理的請求數。爲什麽說模糊呢?因爲這個數據受實際測試環境,流量場景模式等衆多因素影響,你很難複現這個測試數據。即便這個20%的性能提升是確實的,其實這個性能提升也並不大,和異步引入的複雜性相比,這20%的提升是否值得是個問題。Netflix本身在其博文22和ppt11中也是有點含糊其詞,甚至自身都有一些疑問的。

(5)Spring Cloud Gateway

SpringCloud Gateway 是 Spring Cloud 的一個全新項目,該項目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術開發的網關,它旨在爲微服務架構提供一種簡單有效的統一的 API 路由管理方式。

SpringCloud Gateway 作爲 Spring Cloud 生態系統中的網關,目標是替代 Zuul,在Spring Cloud 2.0以上版本中,沒有對新版本的Zuul 2.0以上最新高性能版本進行集成,仍然還是使用的Zuul 2.0之前的非Reactor模式的老版本。而爲了提升網關的性能,SpringCloud Gateway是基于WebFlux框架實現的,而WebFlux框架底層則使用了高性能的Reactor模式通信框架Netty。

Spring Cloud Gateway 的目標,不僅提供統一的路由方式,並且基于 Filter 鏈的方式提供了網關基本的功能,例如:安全,監控/指標,和限流。

Spring Cloud Gateway 底層使用了高性能的通信框架Netty 。

SpringCloud Gateway 特征

SpringCloud官方,對SpringCloud Gateway 特征介紹如下:

基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0集成 Hystrix 斷路器集成 Spring Cloud DiscoveryClientPredicates 和 Filters 作用于特定路由,易于編寫的 Predicates 和 Filters具備一些網關的高級功能:動態路由、限流、路徑重寫

從以上的特征來說,和Zuul的特征差別不大。SpringCloud Gateway和Zuul主要的區別,還是在底層的通信框架上。

簡單說明一下上文中的三個術語:

Filter (過濾器)

和Zuul的過濾器在概念上類似,可以使用它攔截和修改請求,並且對上遊的響應,進行二次處理。過濾器爲org.springframework.cloud.gateway.filter.GatewayFilter類的實例。

Route (路由)

網關配置的基本組成模塊,和Zuul的路由配置模塊類似。一個Route模塊 由一個 ID,一個目標 URI,一組斷言和一組過濾器定義。如果斷言爲真,則路由匹配,目標URI會被訪問。

Predicate (斷言):

這是一個 Java 8 的 Predicate,可以使用它來匹配來自 HTTP 請求的任何內容,例如 headers 或參數。斷言的 輸入類型是一個 ServerWebExchange。

0 阅读:27

數據智能相依偎

簡介:感謝大家的關注