線上SpringCloud網關調用微服務跨機房了,咋整?

来源:https://www.cnblogs.com/ldws/archive/2020/02/28/12379994.html
-Advertisement-
Play Games

1、前言 公司內考慮到伺服器資源成本的問題,目前業務上還在進行服務的容器化改造和遷移,計劃將容器化後的服務,以及一些中間件(MQ、DB、ES、Redis等)儘量都遷移到其他機房。 那你們為什麼不用阿裡雲啊,騰訊雲啊,還用自己的機房? 的確是這樣,公司內部目前還是有專門的運維團隊。也是因為歷史原因,當 ...


1、前言

公司內考慮到伺服器資源成本的問題,目前業務上還在進行服務的容器化改造和遷移,計劃將容器化後的服務,以及一些中間件(MQ、DB、ES、Redis等)儘量都遷移到其他機房。

那你們為什麼不用阿裡雲啊,騰訊雲啊,還用自己的機房?

的確是這樣,公司內部目前還是有專門的運維團隊。也是因為歷史原因,當時業務發展比較迅猛,考慮到數據的安全性也是自建機房的。對於中小型公司這樣做,顯然成本太高了,所以一般都用阿裡雲。對於中大型企業或者對數據安全性要求高的公司,自建機房維護的也不再少數。

對於中間件來說,比如 Redis 緩存,有的業務也是因為歷史原因,當時上線後都是單獨申請,並部署的一套集群,但是量並不是很大,所以類似這種情況的,可以考慮跟其他項目使用的集群合併為一個,這樣就可能節省了一部分伺服器資源。

現在大多數企業都已經微服務化,容器化了。

所以,將非容器化的業務要求都遷移到容器中,這裡的容器基本都是指 Kubernetes 平臺了,通過容器發佈調度服務,對於運維來說,維護變得更加便捷,高效。

對於研發來說,業務需要部署服務,不再需要重新提 JIRA 工單,走一系列審核流程,最後給到你的可能還是一臺虛擬機,依賴的軟體單獨安裝部署。用了容器,只要在 集裝箱 中提前安裝好所需軟體環境,按照發佈規範打好鏡像,發佈服務的過程一路就是 點點點...

docker集裝箱

2、線上業務場景介紹

繼續來說今天的主題。

有一個項目是 SpringCloud 架構的,其中使用到了 網關 Zuul,並且也使用了到了 Eureka 作為註冊中心。

因為該項目提前已經遷移到北京機房節點部署的容器環境,我們最終目標是遷移到其他機房(如:天津機房)。

北京有兩個機房:A機房、B機房,因為都在北京,所以兩個機房之間的 網路延時 是可以接受的。

微服務也同樣在這兩個機房之間都有部署。

北京A-B機房

此時,如果只是將微服務部署到 天津機房,會變成如下圖所示的關係:

微服務部署到天津

問題很明顯,就是網關服務只有北京的,而微服務新增了天津機房的,此時會導致 跨機房調用,即北京網關調用到了天津微服務。

儘管北京到天津 ping 的網路延時僅有 3 毫秒 之差,但是服務與服務之間的調用,可就不止這 3 毫秒了。

其中包括伺服器與伺服器之間 TCP連接的建立、數據傳輸的網路開銷,如果數據包過大,跨機房訪問耗時就會很明顯了。

所以呢,儘量避免跨機房訪問,當然要將網關也要遷移到天津機房。

網關部署到天津

但是,大家看 粉紅色粗體 的線條,仍然存在跨機房調用,天津網關調用到北京微服務。

對於線上併發訪問量稍微大點,或者有些介面響應體大的,又或者網路抖動等場景下,可能就會導致介面響應時間變長了。

如何解決呢?

因大部分業務都部署到天津,可以將天津機房的服務權重調高

SLB配置 (類Nginx):

upstream {
    server 北京機房網關IP  20;
    server 天津機房網關IP  80;
}

網關與微服務之間,都是通過 Eureka 註冊中心媒介來溝通,即 註冊服務 拉取服務

僅僅在網關層配置好權重還不夠,此時還會存在天津網關路由到北京微服務上。

Eureka 內部是基於 Ribbon 實現負載均衡的,自行實現按權重的負載均衡策略,Eureka做一點改造,界面上支持權重的修改。

下圖截圖了部分示例:

file

IP後面的就是權重值,可以在界面上輸入權重值進行調整。

我們可以將北京微服務權重調低,天津微服務權重調高。

相當於網關以及微服務兩側都是通過基於 權重 的負載均衡演算法來儘量減少跨機房調用的,但是無法避免跨機房調用。

使用 Eureka 的分區改進

上面描述的方案對於 20% 的流量仍然存在跨機房訪問,我們能不能做到先訪問同一機房的服務,如果同一機房的服務都不可用了,再訪問其他機房的呢?

答案是 可以的

我們可以藉助於 Eureka 註冊中心裡提供了 regionzone 的概念來實現。

regionzone 兩個概念均來自亞馬遜的 AWS:

region:簡單理解為地理上的分區,比如亞洲地區,或者華北地區等等,沒有具體大小的限制。根據項目情況,自行合理劃分 region。

zone:簡單理解為 region 內的具體機房,比如說 zone 劃分為北京、天津,且北京有兩個機房,就可以在 region 內劃分為三個zone,北京劃分為zone1、zone2,天津為zone3。

結合上面的示例,假設僅設置一個 region 為京津地區。

然後我們給這個區域下的網關服務、微服務打上 zone 機房標簽,在系統運維上將機房也稱作 IDC 數據中心。

網關服務打上zone標簽:

網關zone

微服務打上zone標簽:

微服務zone

這個功能都是在 Eureka註冊中心 上實現的,在給服務配置 zone 前,調用路徑如下所示:

微服務架構

給服務配置 zone 之後,框架內部的路由機制的實現下,調用路徑如下所示:

微服務架構zone

當前使用的 Eureka 是部署在北京,如果想讓服務在註冊續約拉取 動作時也能實現 就近機房訪問,部署架構就變成如下這個樣子:

微服務架構改進zone

北京區域不同機房假設認為網路延時小,所以北京兩個機房可以使用同一個 Eureka 集群;天津可以單獨再部署一套 Eureka 集群,這樣就可以實現優先路由到同機房訪問。

服務註冊的關鍵配置

基本原理就是這樣,貼上一段 Eureka 使用 regionzone 的配置供大家參考:

spring:
  application:
    name: mananger
 
server:
  port: ${EUREKA_SERVER_PORT:8011}
 
eureka:
  instance: 
    # 全網服務實例唯一標識
    instance-id: ${EUREKA_SERVER_IP:127.0.0.1}:${server.port}
    # 服務實例的meta數據鍵值對集合,可由註冊中心進行服務實例間傳遞
    metadata-map:
      # [HA-P配置]-當前服務實例的zone
      zone: ${EUREKA_SERVER_ZONE:tz-1}
      profiles: ${spring.profiles.active}
    # 開啟ip,預設為false=》hostname
    prefer-ip-address: true
    ip-address: ${EUREKA_SERVER_IP:127.0.0.1}
    # [HA-P配置]-當前服務實例的region
client:
    region: ${EUREKA_SERVER_REGION:cn-bj}
    # [HA-P配置]-開啟當前服務實例優先發現同zone的註冊中心,預設為true
    prefer-same-zone-eureka: true
    # [服務註冊]-允許當前服務實例註冊,預設為true
    register-with-eureka: true
    # [服務續約]-允許當前服務實例獲取註冊信息,預設為true
    fetch-registry: true
    # [HA-P配置]-可用region下zone集合
        availability-zones:
      cn-bj: ${eureka.instance.metadata-map.zone},zone-bj,zone-tj
   service-url:  
      # [HA-P配置]-各zone下註冊中心地址列表
       zone-bj: http://BJIP1:8011/eureka,http://BJIP2:8012/eureka
       zone-tj: http://TJIP1:8013/eureka,http://TJIP2:8014/eureka

prefer-same-zone-eureka :

預設就為true,首先會通過 region 找到 availability-zones 內的第一個 zone,然後通過這個 zone 找到 service-url 對應該機房的註冊中心地址列表,並向該列表內的 第一個URL 地址發起註冊和心跳,不會再向其它的URL地址發起操作。只有當第一個URL地址註冊失敗的情況下,才會依次向其它的URL發起操作,重試一定次數仍然失敗,會間隔一段心跳時間繼續重試。

eureka.instance.metadata-map.zone:

服務提供者和消費者都要配置該參數,表示自己屬於哪一個機房的。網關服務也屬於消費者,從註冊中心拉取到註冊表之後會根據這個參數中指定的 zone 進行過濾,過濾後向同 zone 內的服務會有多個實例 ,通過 Ribbon 來實現負載均衡調用。如果同一 zone 內的所有服務都不可用時,會其他 zone 的服務發起調用。

另外註意一點 availability-zones 下 region 的配置是 ${eureka.instance.metadata-map.zone},... 這樣配置的好處是,你只要指定好了 eureka.instance.metadata-map.zone,優先會將這個參數放到可用分區下作為第一個 zone 來訪問。

Zuul 網關路由分區源碼分析

網關使用的 zuul,其內部也是通過 ribboneureka 的結合來實現服務之間的調用,因為網關實際也是個服務消費者,同樣會註冊到 eureka 上,被網關拉取過來的註冊表裡的服務,作為服務提供者,同樣會註冊到eureka上。

通過一張圖把控整個請求的大致脈絡:

網關請求源碼脈絡

上述圖示中部分核心源碼如下所示:

PollServerListUpdater#start(final UpdateAction action) 啟動後會每隔30秒(預設)去Eureka註冊中心拉取一次註冊表信息,更新本地緩存的數據結構。

PollServerListUpdater#start

調用到了DyamicServerListLoadBalancer匿名實現類中。

DyamicServerListLoadBalancer

通過DyamicServerListLoadBalancer類調用了 updateListOfServer() 方法更新服務列表,serverListImpl的實現是DiscoveryEnabledNIWSServerList類
DyamicServerListLoadBalancer#updateListOfServers

在DiscoveryEnabledNIWSServerList類內部會調用 obtainServersViaDiscovery() 方法,其內部通過 EurekaClient 來實現從 Eureka 註冊中心拉取服務列表。

DiscoveryEnabledNIWSServerList#getUpdatedListOfServers

過濾器內部獲取同一機房(zone)的服務列表,先後會調用 ZonePreferenceServerListFilterZoneAffinityServerListFilter 兩個過濾器實現 zone 的過濾。

ZoneAffinityServerListFilter#getFilteredListOfServers

最開始獲取的Servers一共是有4條記錄,根據調試的代碼看,我們是為了獲取 zone 為2的服務,所以得到的結果是一條,即 zone = "2",說明找到了同 zone 服務。

請求介面後會調用到 LoadBalancerContext#getServerFromLoadBalancer(...),內部會調用到ILoadBalancer 具體實現的 chooseServer() 方法,最終會獲取到 zone="2" 里的一個Server。
LoadBalancerContext#getServerFromLoadBalancer

那麼這裡是如何選擇的Server呢?

本地調試時,只配置了已給可用的zone,所以這裡條件滿足會直接調用 super.chooseServer(key) 父類的方法:

file

BaseLoadBalancer#chooseServer(...) 父類的選擇Server的方法,其內部通過 IRule#choose(key) 會調用到具體的負載均衡器的實現:

file

上述截圖中,能看到 MetadataWeightedRule ,這個類是我們自行基於權重負載均衡實現。

file

該實現類是繼承了 ZoneAviodanceRule ,目的就是利用了 zone 的概念,所重寫的 choose(Object key) 方法,調用了 this.getPredicate().getEligibleServers(...) 會走同樣的過濾規則獲取到同一機房(zone)下的所有服務列表,然後在基於每個服務配置的權重篩選一個Server。

獲取到 Server 後,拼接介面的URI請求地址 http://IP:PORT/api/.../xxx.json ,通過底層的 OkHttp 實現完成 Http 介面的調用過程。

底層Http請求

好了,到此基本就分析完了,從網關請求,通過 ribbon 組件從 eureka 註冊中心拉取服務列表,如何基於 zone 分區來實現多數據中心的訪問。

對於 服務註冊,要保證服務能註冊到同一個 zone 內的註冊中心,如果跨 zone 註冊,會導致網路延時較大,出現拉取註冊表,心跳超時等問題。

對於 服務調用,要保證優先調用同一個 zone 內的服務,當無法找到同 zone 或者 同 zone 內的服務不可用時,才會轉向調用其他 zone 里的服務。

本文提到的只是網關到微服務之間的調用,實際項目中,微服務還會調用其他第三方的服務,也要同時考慮到跨機房調用的問題,儘量都讓各服務之間在同機房調用,減少網路延時,提高服務的穩定性。

歡迎關註我的公眾號,掃二維碼關註獲得更多精彩原創文章,與你一同成長~

Java愛好者社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [TOC] 這裡對 C 語言的指針進行比較詳細的整理總結,參考網路上部分資料整理如下。 指針概念 電腦中所有的數據都必須放在記憶體中,不同類型的數據占用的位元組數不一樣,例如 int 占用4個位元組,char 占用1個位元組。為了正確地訪問這些數據,必須為每個位元組都編上號碼,就像門牌號、身份證號一樣,每個 ...
  • 在pandas中怎麼樣實現類似mysql查找語句的功能: pandas中獲取數據的有以下幾種方法: 布爾索引 位置索引 標簽索引 使用API 假設數據如下: 布爾索引 該方法其實就是找出每一行中符合條件的真值(true value),如找出列A中所有值等於foo 位置索引 使用iloc方法,根據索引 ...
  • this是指當前對象 this.widget是指當前組件 比如我有一個 的組件ShapeStep,在_ShapeStepState中的this.widget才能訪問到str屬性,而this訪問不到。 ...
  • package com.cnblogs.test; import java.util.List; import java.util.Map; import com.google.common.collect.ImmutableMap; import com.google.common.collect ...
  • go 語言中文網(每日資訊)\_2020 02 28 一、Go 語言中文網 1. "Gopher 學習效率低怎麼辦?曹大談工程師應該怎麼學習 " 2. "Go 的 http 包中預設路由匹配規則" 3. "【每日一庫】Web 表單驗證利器:go playground/validator" 4. "從 ...
  • 此教程已支持最新2019.3版本 本教程適用Windows、Mac、Ubuntu等所有平臺。 激活前準備工作 配置文件修改已經不在bin目錄下直接修改,而是通過Idea修改 如果輸入code一直彈出來,請去hosts里移除jetbrains相關的項目 激活出現問題自己無法解決可在關註公眾號回覆 :i ...
  • 個人簡介 ​ Java後臺開發方向。 非電腦專業碩士,專業涉及到一些開發。 實驗室項目主要是Java Web系統,挖掘小亮點。 無實習經驗。 閑話嘮嗑 ​ 回顧這幾個月,宛若夢一場。 一開始心態不好,看到要學習的東西一大堆,沉不下心來學習,看什麼東西都是看著看著就很浮躁,開始瘋狂抖腿,沉迷幻想,以 ...
  • 授權 授權,也叫訪問控制,即在應用中控制誰能訪問哪些資源(如訪問頁面/編輯數據/頁面操作等)。在授權中需瞭解的幾個關鍵對象:主體(Subject)、資源(Resource)、許可權(Permission)、角色(Role)。 主體 主體,即訪問應用的用戶,在 Shiro 中使用 Subject 代表該 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...