Nginx系列教程(五)| 利用 Nginx+Keepalived 實現高可用技術 | 週末學習

做者:JackTianhtml

微信公衆號:傑哥的IT之旅(ID:Jake_Internet)nginx

這是我參與更文挑戰的第 5 天,活動詳情查看:更文挑戰web


1、什麼是高可用?

高可用(High Availability)是分佈式系統架構設計中必須考慮的因素之一,一般是指:經過設計從而減小系統不能提供服務的時間redis

2、怎麼來衡量高可用?

舉個例子,好比說一個系統它一直可以爲你提供服務,那它的系統可用性就是100%,當系統運行到100個時間單位時,可能會有1-2個時間單位沒法爲你提供服務,那它的系統可用性就是99%98%,在一年的時間內保證99%可用性的系統最多能夠有3.65天的停機時間(1%)。這些值根據幾個因素計算的,包括計劃非計劃維護週期,以及從可能的系統故障中恢復的時間算法

目前大部分企業的高可用目標是4個9,也就是99.99%,有幾個 9,就表明了你的可用性。數據庫

  • 2個9:基本可用,網站年度不可用時間小於 88 小時;
  • 3個9:較高可用,網站年度不可用時間小於 9 小時;
  • 4個9:具備自動恢復能力的高可用,網站年度不可用時間小於 53 分鐘;
  • 5個9:極高可用,也就是很理想的狀態,網站年度不可用時間小於 5 分鐘;

可用性的9怎麼計算出來的呢?json

  • 網站不可用時間 = 故障修復時間點 - 故障發現時間點
  • 網站年度可用性指標 =(1 - 網站不可用時間/年度總時間)* 100%

可用性的考覈:網站可用性,跟技術、運營、等各方面的績效考覈相關,所以在前期的架構設計中,關於系統高可用性的問題也會話很大一部分時間,互聯網企業不一樣公司有着不一樣的策略,每每由於種種因素會直接影響到系統的高可用性,業務增加較快的網站同時也將面臨着用戶的增加率,同時也慢慢會下降高可用性的標準,所以也就會對網站作一些相關性的策略或後端設備的支持等;vim

通常都是採用故障來分的,也是對網站故障進行分類加權計算故障責任的方法。通常會給每一個分類的故障設置一個權重(例如事故級故障權重爲100,A類爲20等),計算公式爲:故障分=故障時間(分鐘)* 故障權重後端

3、高可用網站架構設計目的是什麼?

當服務器的集羣設備頻繁讀寫時,會致使硬件出現故障的現象。瀏覽器

高可用架構設計的目的:保證服務器硬件故障時服務依然可用、數據依然保存並可以被訪問。

4、實現高可用的主要手段有哪些?

  • 數據層面:冗餘備份

一旦某個服務器宕機,就將服務切換到其餘可用的服務器上;

冗餘備份分爲:冷備份熱備份

冷備份是按期複製,不能保證數據可用性。

熱備份又分爲異步熱備同步熱備,異步熱備是指:多份數據副本的寫入操做異步完成,同步熱備是指:多份數據副本的寫入操做同時完成。

圖片

  • 服務層面:失效轉移

如某塊磁盤損壞,將從備份的磁盤讀取數據。(首先是已經提早作好了數據同步操做);

若數據服務器集羣中任何一臺服務器宕機時,那麼應用程序針對這臺服務器的全部讀寫操做都要從新路由到其餘服務器,保證數據訪問不會失敗。

圖片

5、高可用的應用

應用層處理網站應用的業務邏輯,最顯著的特色是:應用的無狀態性

無狀態性的應用是:指應用服務器不保存業務的上下文信息,僅根據每次請求提交的數據進行相應的業務邏輯處理,且多個服務實例(服務器)之間徹底對等,請求提交到任意服務器,處理結果都是徹底同樣的。

圖片

1)經過負載均衡進行無狀態服務的失效轉移

不保存狀態的應用是給高可用架構帶來了巨大便利,服務器不保存請求的狀態,全部的服務器徹底對等;

當任意一臺或多臺服務器出現宕機時,請求提交給集羣中的其餘任意一臺可用服務器進行處理,對客戶端用戶來說,請求老是成功的,整個系統依然可用。

對於應用服務器集羣,實現這種服務器可用狀態實時檢測、自動轉移失敗任務的機制就是負載均衡。主要是在業務量和數據量使用頻率較高時,單臺服務器不足以承擔全部的負載壓力,那麼能夠經過負載均衡這種手段,將流量和數據平均到集羣中其餘服務器上,提升總體的負載處理能力。

無論在從此的工做中,是使用開源免費的負載均衡軟件仍是硬件設備,都需具有失效轉移功能,網站應用中,集羣中的服務器是無狀態對等時,負載均衡便可起到事實上高可用的做用。

圖片

當 Web 服務器集羣中的服務器均可用時,負載均衡服務器會把客戶端發送到的訪問請求分發到任意一臺服務器上來進行處理,這時當服務器2出現宕機時,負載均衡服務器經過心跳檢測機制發現該服務器失去響應,就會把它從服務器列表中刪除,而將請求發送到 Web 服務器集羣中的其餘服務器上,這些服務器徹底同樣,請求在任何一臺服務器中處理都不會影響到最終結果。

在實際環境中,負載均衡在應用層起到了系統高可用的做用,即使當某個應用訪問量較少時,只用一臺服務器足以支撐並提供服務,一旦須要保證該服務高可用時,必須至少部署兩臺服務器,從而使用負載均衡技術搭建一個小型的 Web 服務器集羣。

2)應用服務器集羣的Session管理

Web 應用中將屢次請求修改使用的上下文對象稱爲會話(Session),單機狀況下,Session 可部署在服務器上得 Web 容器(如 IIS、Tomcat 等)管理。

在使用了負載均衡的集羣環境中,負載均衡服務器可能會將請求分發到 Web 服務器集羣中的任何一臺應用服務器上,因此保證每次請求可以得到正確的 Session 比單機時要複雜得多。

在集羣環境中,Session 管理的幾種常見手段:

  • Session 複製

Session 複製:簡單易行,是早期企業應用系統使用較多的一種服務器集羣 Session 管理機制。應用服務器開啓 Web 容器的 Session 複製功能,在集羣中的其餘服務器之間將會同步 Session 對象,與其使得每臺服務器上都將會保存全部用戶的 Session 信息。

當集羣中的任何一臺服務器出現宕機時,都不會致使 Session 數據的丟失,而服務器使用 Session 時,也只須要在本機獲取便可。

Session 複製這種方案只適合集羣規模較小的環境,當規模較大時,大量的 Session 複製操做會佔用服務器和網絡的大量資源,系統也將面臨很大的壓力。

全部用戶的 Session 信息在每臺服務器上都有備份,當大量用戶訪問時,甚至會出現服務器內存不夠 Session 使用的狀況,大型網站的核心應用集羣都是數千臺服務器以上,同時在線用戶可達上千萬,並不適合用 Session 複製這種方案。

圖片

  • Session 綁定

Session 綁定是利用負載均衡的源地址 Hash 算法實現的,負載均衡服務器老是未來源於同一 IP 的請求分發到同一臺服務器上,在整個會話期間,用戶全部的請求都在同一臺服務器上處理,Session 綁定在某臺特定服務器上,保證 Session 總能在這臺服務器上獲取,所以這種方法被稱做會話粘滯。

但 Session 綁定這種方案不符合對於系統高可用的需求,一旦某臺服務器出現宕機,那麼該機器上的 Session 也將不存在了,用戶請求切換到其餘服務器上後,所以沒有 Session 也將沒法完成業務處理,大部分負載均衡服務器都提供源地址負載均衡算法,但不多有網站利用這個算法進行 Session 管理。

圖片

  • Cookie 記錄 Session

早期企業應用系統使用 C/S(客戶端/服務器)架構,一種管理 Session 的方式是將 Session 記錄在客戶端,客戶端請求服務器的時候,將 Session 放在請求中發送給服務器,服務器處理完請求後再將修改過的 Session 響應給客戶端。由於網站沒有客戶端,所以利用瀏覽器支持的 Cookie 記錄 Session。

利用瀏覽器支持的 Cookie 記錄 Session 缺點:

  • 受 Cookie 大小限制,能記錄的信息有限
  • 每次請求響應都須要傳輸 Cookie ,影響性能
  • 如用戶關閉 Cookie ,訪問將會不正常

Cookie 簡單易用,可用性高,支持應用服務器的線性伸縮,大部分應用須要記錄的 Session 信息比較小。所以許多網站都將會使用 Cookie 來記錄 Session。

圖片

  • Session 服務器

利用獨立部署的 Session 服務器或集羣統一管理 Session ,應用服務器每次讀寫 Session 時,都將會訪問 Session 服務器。實際上是將應用服務器的狀態進行分離爲:無狀態的應用服務器有狀態的 Session 服務器,針對這兩種服務器的不一樣特性分別設計其架構。

對於有狀態的 Session 服務器是利用分佈式緩存、數據庫等,在這些產品的基礎上進行封裝,使其符合 Session 的存儲和訪問要求。若是業務場景對 Session 管理有比較高的要求可利用 Session 服務集成單點登陸、用戶服務等功能,則需專門開發 Session 服務管理平臺。

圖片

6、高可用的服務

高可用的服務是用的服務模塊爲:業務產品提供基礎公共服務,在大型網站中這些服務一般都獨立分佈式部署,被具體應用遠程調用。可複用的服務和應用同樣,是無狀態的服務,可以使用相似負載均衡的失效轉移策略實現高可用的服務。

在具體實踐中,高可用的幾點服務策略:

  • 分級管理:運維上將服務器進行分級管理,核心應用和服務優先使用更好的硬件,在運維響應速度上也格外迅速,同時在服務部署上也進行必要的隔離,避免故障的連鎖反應,低優先級的服務經過啓動不一樣的線程或者部署在不一樣的虛擬機上進行隔離,而高優先級的服務則須要部署在不一樣的物理機上,核心服務和數據甚至須要部署在不一樣地域的數據中心。

  • 超時設置:在應用程序中設置服務調用的超時時間,一旦超時後,通訊框架拋出異常,應用程序則根據服務調度策略選擇重試或將請求轉移到提供相同服務的其餘服務器上;

  • 異步調用:經過消息隊列等異步方式完成,避免一個服務失敗致使整個應用請求失敗的狀況;

  • 服務降級:網站訪問高峯期間,服務到大量併發調用時,性能會降低,可能會致使服務宕機,爲保證核心應用及功能可以正常運行,須要對服務降級;

降級有兩種手段:

一:拒絕服務,拒絕較低優先級的應用的調用,減小服務調用併發數,確保核心應用的正常運行;

二:關閉功能,關閉部分不重要的服務,或者服務內部關閉部分不重要的功能,以節約系統開銷,爲核心應用服務讓出資源;

  • 冪等性設計:應用調用服務失敗後,會將調用請求從新發送到其餘服務器,服務重複調用時沒法避免的,應用層其實不關心你服務是否真的失敗,只要沒有收到調用成功的響應,就認爲調用失敗,並重試服務調用。所以必須在服務層保證服務重複調用和調用一次產生的結果相同,即服務具備冪等性

7、常見的互聯網分層架構

圖片

整個系統的高可用,又是經過每一層的冗餘+自動故障轉移來綜合實現,而常見互聯網分佈式架構如上,分爲:

  • 客戶端層:典型調用方是瀏覽器 browser 或者手機應用 APP
  • 反向代理層:系統入口,反向代理
  • 站點應用層:實現核心應用邏輯,返回 html 或者 json
  • 服務層:若是實現了服務化,就有這一層
  • 數據-緩存層:緩存加速訪問存儲
  • 數據-數據庫層:數據庫固化數據存儲

8、分層高可用架構

客戶端層 --> 反向代理層的高可用

圖片

客戶端層到反向代理層的高可用,經過反向代理層的冗餘來實現。以 Nginx 服務爲例:需準備兩臺 Nginx,一臺對線上提供服務,另外一臺作冗餘保證高可用,常見的實踐是keepalived存活探測,相同虛擬 IP(virtual IP)來提供服務。

圖片

自動故障轉移:當一臺Nginx宕機時,Keepalived可以檢測到,會自動的將故障進行轉移,使用的是相同的虛擬IP,切換過程對調用方是透明的。

反向代理層 --> 站點層的高可用

圖片

反向代理層到站點層的高可用,經過站點層的冗餘來實現,反向代理層是Nginx,Nginx.conf 裏可以配置多個 Web 後端,而且 Nginx 可以檢測多個後端的存活性。

圖片

故障自動轉移:當 Web-server 宕機時,Nginx 可以檢測到,會自動進行故障轉移,將流量自動轉移到其餘的 Web-server,整個過程由 Nginx 自動完成,對調用方是透明的。

站點層 --> 服務層的高可用

圖片

站點層到服務層的高可用,是經過服務層的冗餘來實現的。「服務鏈接池」會創建與下游服務多個鏈接,每次請求會「隨機」選取鏈接來訪問下游服務。

圖片

故障自動轉移:當 service 宕機時,service-connection-pool 可以檢測到,會自動的進行故障轉移,將流量自動轉移到其餘的 service,整個過程由鏈接池自動完成,對調用方是透明的(RPC-client 中的服務鏈接池是很重要的基礎組件)。

服務層 --> 緩存層的高可用

圖片

服務層到緩存層的高可用,是經過緩存數據的冗餘來實現,緩存層的數據冗餘可經過利用客戶端的封裝,service 對 cache 進行雙讀或者雙寫方式。

圖片

緩存層也能夠經過支持主從同步的緩存集羣來解決緩存層的高可用問題,redis 自然支持主從同步,redis也有 sentinel 機制,來作 redis 的存活性檢測。

圖片

自動故障轉移:當 redis 主掛了的時候,sentinel 可以檢測到,會通知調用方訪問新的redis,整個過程由 sentinel 和 redis 集羣配合完成,對調用方是透明的。

服務層 --> 數據庫層的高可用,大部分互聯網數據庫層都將採用了主從複製,讀寫分離架構,因此數據庫層的高可用又分爲讀庫高可用寫庫高可用兩類。

服務層 --> 數據庫層的高可用

圖片

服務層到數據庫讀的高可用,是經過讀庫的冗餘來實現,冗餘了讀庫,通常來講就至少有2個從庫,數據庫鏈接池會創建與讀庫多個鏈接,每次請求會路由到這些讀庫裏面去。

圖片

故障自動轉移:當一臺讀庫宕機時,db-connection-pool 可以檢測到,會自動的進行故障轉移,將流量自動遷移到其餘的讀庫,整個過程由鏈接池自動完成,對調用方是透明的,數據庫鏈接池是很重要的基礎組件。

服務層 --> 數據庫層的高可用

圖片

服務層到數據庫寫的高可用,是經過寫庫的冗餘來實現,能夠設置兩臺MySQL雙主同步,一臺對線上提供服務,另外一臺作冗餘以保證高可用,常見的實踐是keepalived存活探測,相同虛擬IP(virtual IP)提供服務。

圖片

故障自動轉移:當寫庫宕機時,keepalived可以檢測到,會自動的進行故障轉移,將流量自動遷移到shadow-db-master,使用的是相同的虛擬IP(virtual IP),這個切換過程對調用方是透明的。

9、配置高可用的準備工做

圖片

一、 準備兩臺 Nginx 服務器(IP 地址:192.168.1.10 和 192.168.1.11),並在兩臺 Nginx 服務器上安裝Keepalived,以及配置虛擬 IP 地址

二、 192.168.1.10 服務器,由於咱們前期就已經安裝好了 Nginx,無須在從新安裝了,只需在 192.168.1.11 設備上安裝 Nginx 服務便可,詳細能夠查看這篇文章:《Nginx系列教程(一)| 手把手教你在Linux環境下搭建Nginx服務》;

三、 分別在兩臺Nginx服務器上安裝Keepalived服務,可經過 rpm 包或 yum 一鍵安裝,這兩種安裝方式都是能夠的,根據我的所需安裝便可;

# rpm -ivh /mnt/Packages/keepalived-1.2.7-3.el6.x86_64.rpm 
# yum -y install keepalived
複製代碼

四、 在兩臺Nginx服務器上分別啓動Nginx服務和keepalived服務;

# cd /usr/local/nginx/sbin
# ./nginx
# service keepalived start
正在啓動 keepalived:                                      [肯定]
複製代碼

五、 在客戶端瀏覽器中分別輸入192.168.1.10192.168.1.11進行驗證是否可以正常訪問Nginx服務;

圖片

10、配置高可用的主備模式實操案例

主備方案:這種方案也是目前企業中最經常使用的一種高可用的方案,簡單來講,就是指一臺服務器在提供服務時,另外一臺服務器爲其餘服務且是備用狀態,當一臺服務器出現宕機時,將自動跳轉至備用服務器上,所以客戶端所發出的請求將不會出現有失敗現象。

在上述的準備工做介紹到了本次配置高可用將採用Keepalived來實現,那麼什麼是Keepalived?

Keepalived是一款服務器狀態檢測和故障切換的工具,起初是專爲LVS負載均衡軟件設計的,用來管理監控LVS集羣系統中各個服務節點的狀態,後來又加入了能夠實現高可用的VRRP (Virtual Router Redundancy Protocol ,虛擬路由器冗餘協議)功能。

所以,Keepalived除了可以管理LVS軟件外,還能夠做爲其餘服務(例如:Nginx、Haproxy、MySQL等)的高可用解決方案軟件在其配置文件中,能夠配置主備服務器和該服務器的狀態檢測請求。也就是說keepalived能夠根據配置的請求,在提供服務期間不斷向指定服務器發送請求,若是該請求返回的狀態碼是200,則表示該服務器狀態是正常的,若是不正常,那麼Keepalived就會將該服務器給下線掉,而後將備用服務器設置爲上線狀態,而當主服務器節點恢復時,備服務器節點會釋放主節點故障時自身接管的 IP 資源及服務,恢復到原來的備用角色。

一、 主服務器上配置keepalived.conf配置文件

# vim /etc/keepalived/keepalived.conf 

  1 global_defs {
  2    notification_email {
  3      acassen@firewall.loc
  4      failover@firewall.loc
  5      sysadmin@firewall.loc
  6    }
  7    notification_email_from Alexandre.Cassen@firewall.loc
  8    smtp_server 192.168.1.10               # 主服務器 IP 地址
  9    smtp_connect_timeout 30
 10    router_id LVS_DEVEL
 11 }
 12 
 13 vrrp_script chk_http_port {
 14 
 15    script "/usr/local/src/nginx_check.sh" # nginx_check.sh 腳本路徑
 16 
 17    interval 2              # 檢測腳本執行的間隔
 18 
 19    weight 2
 20 
 21 }
 22 
 23 vrrp_instance VI_1 {
 24     state MASTER           # 指定當前節點爲 master 節點
 25     interface eth0         # 這裏的 eth0 是網卡的名稱,經過 ifconfig 或者 ip addr 能夠查看
 26     virtual_router_id 51   # 這裏指定的是虛擬路由 id,master 節點和 backup 節點須要指定同樣的
 27     priority 90            # 指定了當前節點的優先級,數值越大優先級越高,master 節點要高於 backup 節點
 28     advert_int 1           # 指定發送VRRP通告的間隔,單位是秒
 29     authentication {
 30         auth_type PASS     # 鑑權,默認經過
 31         auth_pass 1111     # 鑑權訪問密碼
 32     }
 33     virtual_ipadress {
 34          192.168.1.100     # 虛擬 IP 地址
 35     }
 36 }
複製代碼

二、 從服務器上配置keepalived.conf配置文件

# vim /etc/keepalived/keepalived.conf 

  1 global_defs {
  2    notification_email {
  3      acassen@firewall.loc
  4      failover@firewall.loc
  5      sysadmin@firewall.loc
  6    }
  7    notification_email_from Alexandre.Cassen@firewall.loc
  8    smtp_server 192.168.1.10               # 主服務器 IP 地址
  9    smtp_connect_timeout 30
 10    router_id LVS_DEVEL
 11 }
 12 
 13 vrrp_script chk_http_port {
 14 
 15    script "/usr/local/src/nginx_check.sh"  # nginx_check.sh 腳本路徑
 16 
 17    interval 2              # 檢測腳本執行的間隔
 18 
 19    weight 2
 20 
 21 }
 22 
 23 vrrp_instance VI_1 {
 24     state BACKUP           # 指定當前節點爲 BACKUP 節點
 25     interface eth1         # 這裏的 eth0 是網卡的名稱,經過 ifconfig 或者 ip addr 能夠查看
 26     virtual_router_id 51   # 這裏指定的是虛擬路由 id,master 節點和 backup 節點須要指定同樣的
 27     priority 80            # 指定了當前節點的優先級,數值越大優先級越高,master 節點要高於 backup 節點
 28     advert_int 1           # 指定發送VRRP通告的間隔,單位是秒
 29     authentication {
 30         auth_type PASS     # 鑑權,默認經過
 31         auth_pass 1111     # 鑑權訪問密碼
 32     }
 33     virtual_ipadress {
 34          192.168.1.100     # 虛擬 IP 地址
 35     }
 36 }
複製代碼

三、nginx_check.sh腳本分別放置在兩臺Nginx服務器的/usr/local/src/目錄下,用於經過Keepalived來檢測Nginx主服務器是否還活着,若是是已經宕機了,將自動切換至從服務器上面去。

# vi /usr/local/src/nginx_check.sh 
 1 #!/bin/bash
 2 A=`ps -C nginx ¨Cno-header |wc -l`
 3 if [ $A -eq 0 ];then
 4     /usr/local/nginx/sbin/nginx
 5     sleep 2
 6     if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then
 7         killall keepalived
 8     fi
 9 fi
複製代碼

四、 在兩臺Nginx服務器上分別配置虛擬 IP 地址,對客戶端的響應是真實服務器直接返回給客戶端的,而真實服務器須要將響應報文中的源 IP 地址修改成虛擬 IP 地址,這裏配置的虛擬 IP 地址就是起這個做用的。

主服務器
# ifconfig eth0:1 192.168.1.100 netmask 255.255.255.0
從服務器
# ifconfig eth1:1 192.168.1.100 netmask 255.255.255.0
複製代碼

五、 在兩臺Nginx服務器上分別重啓Nginx服務和Keepalived服務;

# ./nginx -s stop
# ./nginx
# service keepalived restart
複製代碼

六、  在客戶端瀏覽器中輸入虛擬 IP 地址192.168.1.100測試訪問結果;

圖片

11、模擬主服務器故障驗證高可用的效果

將主服務器的Nginx服務和Keepalived服務,都進行中止。

# ./nginx -s stop
# service keepalived stop
中止 keepalived:                                          [肯定]
複製代碼

經過客戶端瀏覽器再次輸入虛擬 IP 地址 192.168.1.100進行驗證,能夠發現仍是可以正常訪問Nginx服務,也就說明了當主服務器宕機時,將自動切換到從服務器上,所以不受客戶端所訪問形成的影響。

總結

經過本篇文章介紹了什麼是高可用如何來衡量高可用高可用網站架構設計的目的實現高可用的主要手段高可用的應用及服務常見的互聯網分層架構分層高可用架構詳解配置高可用的準備工做主備模式的實操高可用案例以及模擬主服務故障從而來驗證整個高可用的效果。

整個互聯網分層系統架構的高可用,是經過每一層的冗餘+自動故障轉移來實現的,具體的:

  • 【客戶端層】到【反向代理層】的高可用:是經過反向代理層的冗餘實現的,常見實踐是keepalived + virtual IP自動故障轉移;

  • 【反向代理層】到【站點層】的高可用:是經過站點層的冗餘實現的,常見實踐是nginxweb-server之間的存活性探測與自動故障轉移;

  • 【站點層】到【服務層】的高可用:是經過服務層的冗餘實現的,常見實踐是經過service-connection-pool來保證自動故障轉移;

  • 【服務層】到【緩存層】的高可用:是經過緩存數據的冗餘實現的,常見實踐是緩存客戶端雙讀雙寫,或者利用緩存集羣的主從數據同步sentinel自動故障轉移;更多業務場景,對緩存沒有高可用要求,可以使用緩存服務化來對調用方屏蔽底層複雜性;

  • 【服務層】到【數據庫「讀」】的高可用:是經過讀庫的冗餘實現的,常見實踐是經過db-connection-pool來保證自動故障轉移;

  • 【服務層】到【數據庫「寫」】的高可用:是經過寫庫的冗餘實現的,常見實踐是keepalived + virtual IP自動故障轉移;


推薦閱讀

Nginx系列教程(一)| 手把手教你在Linux環境下搭建Nginx服務

Nginx系列教程(二)| 一文帶你讀懂Nginx的正向與反向代理

Nginx系列教程(三)| 一文帶你讀懂Nginx的負載均衡

Nginx系列教程(四)| 一文帶你讀懂Nginx的動靜分離


原創不易,若是你以爲這篇文章對你有點用的話,麻煩你爲本文點個贊、評論或轉發一下,由於這將是我輸出更多優質文章的動力,感謝!

咱們下期再見!

相關文章
相關標籤/搜索