精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

微服務架構—不可或缺的注冊中心

開發 架構
隨著本文的深入探討,我們對微服務架構中的服務發現與注冊機制有了更全面的認識。從單體架構的局限性到微服務的靈活性,我們見證了架構演進的歷程。服務發現與注冊作為微服務通信的基石,其重要性不言而喻。

從今天開始,我們將以Java后端技術為切入點,深入探討微服務架構。本章的重點將聚焦于微服務中最關鍵的環節之一:服務發現與注冊。文章將循序漸進,由淺入深,逐步引領你進入微服務的廣闊世界。不論你是技術新手還是經驗豐富的專家,我都希望通過這篇文章,能夠為你提供獨特而有價值的見解與收獲。

好的,我們開始!

單體架構vs微服務架構

單體架構

首先,我們來看看以前的單體架構。一個歸檔包(例如WAR格式)通常包含了應用程序的所有功能和邏輯,這種結構使得我們將其稱為單體應用。單體應用的設計理念強調將所有功能模塊打包成一個整體,便于部署和管理。這種架構模式被稱為單體應用架構,意指通過一個單一的WAR包來承載整個應用的所有責任和功能。

圖片圖片

正如我們所展示的這張簡單示例圖所示,我們可以更深入地分析單體架構的優缺點,以便全面理解其在軟件開發和系統設計中的影響。

微服務架構

微服務的核心理念是將傳統的單體應用程序根據業務需求進行拆分,將其分解為多個獨立的服務,從而實現徹底的解耦。每個微服務專注于特定的功能或業務邏輯,遵循“一個服務只做一件事”的原則,類似于操作系統中的進程。這樣的設計使得每個服務都可以獨立部署,甚至可以擁有自己的數據庫,從而提高了系統的靈活性和可維護性。

圖片圖片

通過這種方式,各個小服務相互獨立,能夠更有效地應對業務變化,快速迭代開發和發布,同時降低了系統整體的復雜性,這就是微服務架構的本質。當然,微服務架構同樣存在其優缺點,因為沒有任何一種“銀彈”能夠完美解決所有問題。接下來,讓我們深入分析一下這些優缺點:

優點

  • 服務小而內聚:微服務將應用拆分為多個獨立服務,每個服務專注于特定功能,使得系統更具靈活性和可維護性。與傳統單體應用相比,修改幾行代碼往往需要了解整個系統的架構和邏輯,而微服務架構則允許開發人員僅專注于相關的功能,提升了開發效率。
  • 簡化開發過程:不同團隊可以并行開發和部署各自負責的服務,這提高了開發效率和發布頻率。
  • 按需伸縮:微服務的松耦合特性允許根據業務需求對各個服務進行獨立擴展和部署,便于根據流量變化動態調整資源,優化性能。
  • 前后端分離:作為Java開發人員,我們可以專注于后端接口的安全性和性能,而不必關注前端的用戶交互體驗。
  • 容錯性:某個服務的失敗不會影響整個系統的可用性,提高了系統的可靠性。

缺點

  • 運維復雜性增加:管理多個服務增加了運維的復雜性,而不僅僅是一個WAR包,這大大增加了運維人員的工作量,涉及的技術棧(如Kubernetes、Docker、Jenkins等)也更為復雜。
  • 通信成本:服務之間的相互調用需要網絡通信,可能導致延遲和性能問題。
  • 數據一致性挑戰:分布式系統中,維護數據一致性和處理分布式事務變得更加困難。
  • 性能監控與問題定位:需要更多的監控工具和策略來跟蹤各個服務的性能,問題排查變得復雜。

應用場景

所以微服務也并不是適合所有項目。他只適合部分場景這里列舉一些典型案例:

  • 大型復雜項目:微服務架構通過將系統拆分為多個小型服務,降低了每個服務的復雜性,使得團隊能夠更加專注于各自負責的功能模塊,從而顯著提升開發和維護的效率。
  • 快速迭代項目:微服務架構能夠使得不同團隊獨立開發和發布各自的服務,從而實現更高頻率的迭代和更快的市場反應。
  • 并發高的項目:微服務架構則提供了靈活的彈性伸縮能力,各個服務可以根據需求獨立擴展,確保系統在高并發情況下依然能保持良好的性能和穩定性。

好的,關于微服務的基本概念我們已經介紹完畢。接下來,我們將深入探討微服務架構中至關重要的一環:服務注冊與發現。這一部分是微服務生態系統的核心,直接影響到系統的靈活性和可擴展性。

注冊中心

從上面的討論中,我們可以看到,微服務架構的核心在于將各個模塊獨立分開,以實現更好的靈活性和可維護性。然而,這種模塊化設計也帶來了網絡傳輸上的消耗,因此,理解微服務之間是如何進行網絡調用的變得尤為重要。

接下來,我們將逐步探討微服務之間的通信方式,以及這些方式如何影響系統的整體性能。

調用方式

讓我們先思考一個關鍵問題:在微服務架構中,如何有效地維護復雜的調用關系,以確保各個服務之間的協調與通信順暢?

如果你對微服務還不太熟悉,不妨換個角度考慮:我們的電腦是如何實現對其他網站的調用和訪問的?

固定調用

我們最簡單的做法是將 IP 地址或域名硬編碼在我們的代碼中,以便直接進行調用。例如,考慮以下這段代碼示例:

//1:服務之間通過RestTemplate調用,url寫死
String url = "http://localhost:8020/order/findOrderByUserId/"+id;
User result = restTemplate.getForObject(url,User.class);

//2:類似還有其他http工具調用
String url = "http://localhost:8020/order/findOrderByUserId/" + id;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
        .url(url)
        .build();
try (Response response = client.newCall(request).execute()) {
    String jsonResponse = response.body().string();
    // 處理 jsonResponse 對象。省略代碼

從表面上看,雖然將 IP 地址或域名硬編碼在代碼中似乎是一個簡單的解決方案,但實際上這并不是一個明智的做法。就像我們在訪問百度搜索時,不會在瀏覽器中輸入其 IP 地址,而是使用更為便捷和易記的域名。微服務之間的通信同樣如此,每個微服務都有自己獨特的服務名稱。

在這里,域名服務器的作用非常關鍵,它負責存儲域名與 IP 地址的對應關系,從而使我們能夠準確地調用相應的服務器進行請求和響應。微服務架構中也存在類似的機制,這就是我們所說的“服務發現與注冊中心”。可以想象,這個注冊中心就像是微服務的“域名服務器”,它存儲了各個微服務的名稱和它們的網絡位置。

圖片圖片

在配置域名時,我們需要在 DNS 記錄中填寫各種信息;而在微服務的注冊中心中,類似的配置工作也同樣重要,只是通常是在配置文件中完成。當你的服務啟動時,它會自動向注冊中心注冊自己的信息,確保其他服務能夠找到并調用它。

"域名"調用

因此,當我們進行服務調用時,整個過程將變得更加熟悉和直觀。例如,考慮下面這段代碼示例:

//使用微服務名發起調用
String url = "http://mall‐order/order/findOrderByUserId/"+id;
List<Order> orderList = restTemplate.getForObject(url, List.class);

當然,這其中涉及許多需要細致實現的技術細節,但我們在初步理解時,可以先關注服務發現與注冊中心的核心功能。簡而言之,它們的主要目的是為了方便微服務之間的調用,減少開發者在服務通信時所需處理的復雜性。

通過引入服務發現與注冊中心,我們不再需要手動維護大量的 IP 地址與服務名稱之間的關系。

設計思路

作為注冊中心,它的主要功能是有效維護各個微服務的信息,例如它們的IP地址(當然,這些地址可以是內網的)。鑒于注冊中心本身也是一個服務,因此在微服務架構中,它可以被視為一個重要的組件。每個微服務在進行注冊和發現之前,都必須進行適當的配置,才能確保它們能夠相互識別和通信。

這就類似于在本地配置一個DNS服務器,如果沒有這樣的配置,我們就無法通過域名找到相應的IP地址,進而無法進行有效的網絡通信。

圖片圖片

在這個系統中,健康監測扮演著至關重要的角色,其主要目的在于確保客戶端能夠及時獲知服務器的狀態,尤其是在服務器發生故障時,盡管這種監測無法做到完全實時。健康監測的重要性在于,我們的微服務架構中,每個模塊通常會啟動多個實例。盡管這些實例的功能相同,目的在于分擔請求負載,但它們的可用性卻可能有所不同。

圖片圖片

例如,同一個服務名稱可能會對應多個IP地址。然而,如果其中某個IP對應的服務出現故障,客戶端就不應該再嘗試調用這個服務的IP。相反,應該優先選擇其他可用的IP,這樣就能夠有效實現高可用性。

接下來談談負載均衡。在這里需要注意的是,每個服務節點僅將其IP地址注冊到注冊中心,而注冊中心本身并不負責具體調用哪個IP。這一切都完全取決于客戶端的設計和實現。因此,在之前討論域名調用的部分中提到,這里面的細節實際上還有很多。

注冊中心的角色相對簡單,它的主要職責是收集和維護可用的IP地址,并將這些信息提供給客戶端。具體的實現細節和操作流程,可以參考下面的圖片

圖片圖片

實戰

這樣一來,關于系統架構的各個方面,我們基本上都已經有了全面的了解。接下來,我們可以直接進入實踐環節,進行具體的使用演示。在這里,我們將以Spring Cloud Alibaba為例,選擇Nacos作為我們的服務發現與注冊中心。

準備工作

JDK:這是開發必備的基礎環境。

Maven:仍然會用maven進行項目的依賴管理。并啟動Springboot項目。

Nacos Server:你需要自己搭建好一個nacos服務端。

Nacos Docker 快速開始

如果你本身沒有nacos,我建議你可以在本地通過Docker快速搭建一個Nacos實例。具體步驟可以參考官方文檔中的快速入門指南:Nacos Quick Start with Docker。

通過這種方式,你可以在最短的時間內搭建起一個穩定的Nacos服務。

windows 本地

當然,你也可以選擇在本地直接搭建Nacos服務。按照以下步驟進行操作,這里就以此為例進行說明。首先下載:https://github.com/alibaba/nacos/releases

然后本地直接解壓后運行命令即可成功,如下:

startup.cmd -m standalone

圖片圖片

打開本地地址:http://127.0.0.1:8848/nacos/index.html

圖片圖片

Spring Boot 啟動

那么現在,我們可以直接開始啟動本地的兩個服務:一個是用戶模塊,另一個是訂單模塊。此外,我們還將創建一個公共模塊,以便于共享通用的功能和資源。為了簡化演示,我們將編寫最基本的代碼,主要目的是為學習和演示提供一個清晰的框架。我們的項目結構如圖所示:

圖片圖片

首先,公共模塊的主要職責是導入所有服務共享的依賴,這樣可以確保各個模塊之間的一致性和復用性。這里就不演示了。我們只看下order和user模塊的依賴。他倆其實是一樣的,目的就是讓自己的服務注冊到中心去。

<dependencies>
    <dependency>
        <groupId>com.xiaoyu.mall</groupId>
        <artifactId>mall-common</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <scope>compile</scope>
    </dependency>

    <!-- nacos服務注冊與發現 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies>

請添加一些必要的配置文件信息,下面的內容相對簡單。不過,每個服務都需要獨立指定一個微服務名稱,這里僅提供一個示例供參考。

server:
  port: 8040

spring:
  application:
    name: mall-user  #微服務名稱

  #配置nacos注冊中心地址
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: 9f545878-ca6b-478d-8a5a-5321d58b3ca3

命名空間

如果不特別配置命名空間(namespace),則系統會默認將資源部署在公共空間(public)中。在這種情況下,如果需要使用其他命名空間,用戶必須自行創建一個新的命名空間。例如:

圖片圖片

好的,現在我們來啟動這兩個服務,看看運行效果。這樣一來,兩個服務都成功注冊了。不過需要特別注意的是,如果希望這兩個服務能夠相互通信,務必將它們部署在同一個命名空間下。

圖片圖片

我們也可以查看每個服務的詳細信息,這些信息包含了豐富的內容。

圖片圖片

示例代碼

此時,我們并沒有集成任何其他工具,而只是單獨將 Nacos 的 Maven 依賴集成到我們的項目中。在這個階段,我們已經可以通過注解的方式來使服務名稱生效,這樣就無需在代碼中硬編碼 IP 地址。接下來,我們來看看配置類的具體代碼如下:

@Bean
@LoadBalanced  //mall-order => ip:port
public RestTemplate restTemplate() {
    return new RestTemplate();
}

然后,我們可以將用戶端的業務代碼編寫得更加簡潔明了,如下所示:

@RequestMapping(value = "/findOrderByUserId/{id}")
public R  findOrderByUserId(@PathVariable("id") Integer id) {
    log.info("根據userId:"+id+"查詢訂單信息");
    // ribbon實現,restTemplate需要添加@LoadBalanced注解
    // mall-order  ip:port
    String url = "http://mall-order/order/findOrderByUserId/"+id;

    R result = restTemplate.getForObject(url,R.class);
    return result;
}

我們的訂單端業務代碼相對簡單,呈現方式如下:

@RequestMapping("/findOrderByUserId/{userId}")
public R findOrderByUserId(@PathVariable("userId") Integer userId) {
    log.info("根據userId:"+userId+"查詢訂單信息");
    List<OrderEntity> orderEntities = orderService.listByUserId(userId);
    return R.ok().put("orders", orderEntities);
}

我們來看下調用情況,以確認是否確實能夠實現預期的效果。

圖片圖片

第三方組件OpenFeign

在單體架構中,你會直接使用 RestTemplate 類來調用自身的其他服務?顯然是不可能的,因此,在這種情況下,借助流行的第三方組件 OpenFeign 可以顯著簡化服務之間的交互。OpenFeign 提供了一種聲明式的方式來定義 HTTP 客戶端,使得我們可以更方便地進行服務調用,同時保持代碼的可讀性和可維護性。

首先,我們需要在項目的 pom.xml 文件中添加相應的 Maven 依賴。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

初次之外,還需要加一個注解在啟動類上:

@SpringBootApplication
@EnableFeignClients //掃描和注冊feign客戶端bean定義
public class MallUserFeignDemoApplication {、
  public static void main(String[] args) {
      SpringApplication.run(MallUserFeignDemoApplication.class, args);
  }
}

以前寫ip地址那里換成類的時候,我們需要單獨定義一下服務類:

@FeignClient(value = "mall-order",path = "/order")
public interface OrderFeignService {
    @RequestMapping("/findOrderByUserId/{userId}")
    R findOrderByUserId(@PathVariable("userId") Integer userId);
}

這樣一來,我們在調用服務時就可以采用更加簡潔和直觀的寫法。是不是覺得這種方式使用起來更加舒服?

@Autowired
OrderFeignService orderFeignService;

@RequestMapping(value = "/findOrderByUserId/{id}")
public R  findOrderByUserId(@PathVariable("id") Integer id) {
    //feign調用
    R result = orderFeignService.findOrderByUserId(id);
    return result;
}

同樣可以正常調用成功。

圖片圖片

不過,在實施過程中還有一些需要注意的細節。許多開發者傾向于將這些調用封裝到一個單獨的微服務模塊——即 api-service,并將其作為子項目依賴于當前的微服務。這種做法能夠有效地將外部 API 調用與內部服務邏輯進行區分,避免將不同類型的功能混雜在同一個包中。看下:

圖片圖片

好的,到此為止,我們已經完成了一個完整的調用流程。這一切的設置和配置為我們后續的開發奠定了堅實的基礎。接下來,我們就可以專注于實現實際的業務邏輯,比如數據庫的調用與存儲操作。

學習進階

接下來我們將深入探討相關內容。由于許多細節尚未詳盡講解,之前的實戰環節主要旨在讓大家對服務注冊與發現中心的作用有一個初步的理解。為了更好地掌握這一主題,我們需要關注一些關鍵問題,例如客戶端的負載均衡、心跳監測以及服務注冊與發現等。

接下來,我們將通過分析源碼,帶領大家全面了解 Nacos 是如何高效解決注冊中心的三大核心任務的。

gRPC

在這里,我想先介紹一下 Nacos 的實現方式。自 Nacos 2.1 版本起,官方不再推薦使用 HTTP 等傳統的 RPC 調用方式,雖然這些方式仍然是被支持的。如果你計劃順利升級到 Nacos,需特別關注一個配置參數:在 application.properties 文件中設置 nacos.core.support.upgrade.from.1x=true。

在之前的分析中,我們已經探討過 Nacos 1.x 版本的實現,那個版本確實是通過常規的 HTTP 調用進行交互的,Nacos 服務端會實現一些 Controller,就像我們自己構建的微服務一樣,源碼的可讀性非常高,容易理解。調用方式如下面的圖示所示:

圖片圖片

但是,自 Nacos 2.1 版本以來,系統進行了重要的升級,轉而采用了 gRPC。gRPC 是一個開源的遠程過程調用(RPC)框架,最初由 Google 開發。它利用 HTTP/2 作為傳輸協議,提供更高效的網絡通信,并使用 Protocol Buffers 作為消息格式,從而實現了快速且高效的數據序列化和反序列化。

圖片圖片

性能優化:gRPC 基于 HTTP/2 協議,支持多路復用,允許在一個連接上同時發送多個請求,減少延遲和帶寬使用。

二進制負載: 與基于文本的 JSON/XML 相比,協議緩沖區序列化為緊湊的二進制格式。

流控與雙向流:gRPC 支持流式數據傳輸,能夠實現客戶端和服務器之間的雙向流通信,適用于實時應用。

解決 GC 問題:通過真實的長連接,減少了頻繁連接和斷開的對象創建,進而降低了 GC(垃圾回收)壓力,提升了系統性能。

Nacos 升級使用 gRPC 是基于其眾多優點,但我也必須強調,沒有任何技術是所謂的“銀彈”,這也是我一貫的觀點。最明顯的缺點是系統復雜性的增加。因此,在選擇技術方案時,必須根據自身的業務需求做出明智的決策。

在新版 Nacos 的源碼中,你會發現許多以 .proto 后綴命名的文件。這些文件定義了消息的結構,其中每條消息代表一個小的信息邏輯記錄,包含一系列稱為字段(fields)的名稱-值對。這種定義方式使得數據的傳輸和解析變得更加高效和靈活。

例如,我們可以隨便找一個 Nacos 中的緩沖區文件。

圖片圖片

雖然這不是我們討論的重點,但值得指出的是,gRPC 的引入將為 Nacos 帶來顯著的性能優化。盡管我們在這里不深入探討其具體實現,但了解這一點是很重要的,因為在后續的所有調用中,gRPC 都將發揮關鍵作用。

服務注冊

當我們的服務啟動時,會發生一個重要的過程:服務實例會向 Nacos 發起一次請求,以完成注冊。如下圖示:

圖片image

為了提高效率,我們不再逐步進行源碼追蹤,盡管之前已經詳細講解過如何查看 Spring 的自動配置。今天,我們將直接關注關鍵源碼的位置,以快速理解 Nacos 的實現細節。

@Override
public void register(Registration registration) {
  //此處省略非關鍵代碼
    NamingService namingService = namingService();
    String serviceId = registration.getServiceId();
    String group = nacosDiscoveryProperties.getGroup();

    Instance instance = getNacosInstanceFromRegistration(registration);

    try {
        namingService.registerInstance(serviceId, group, instance);
        log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
                instance.getIp(), instance.getPort());
    }
    //此處省略非關鍵代碼

在服務注冊的過程中,我們可以觀察到構建了一些自身的 IP 和端口信息。這些信息對于服務的正確識別和調用至關重要。此外,這里值得一提的是命名空間(Namespace)的概念。命名空間在 Nacos 中用于實現租戶(用戶)粒度的隔離,這對于微服務架構中的資源管理尤為重要。

命名空間的常見應用場景之一是不同環境之間的隔離,比如開發、測試環境與生產環境的資源隔離。

圖片圖片

接下來,我們將進行一個服務調用,這里使用的是 gRPC 協議。實際上,這個過程可以簡化為一個方法調用。

private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass)
        throws NacosException {
    try {
        request.putAllHeader(
                getSecurityHeaders(request.getNamespace(), request.getGroupName(), request.getServiceName()));
        Response response =
                requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout);
        //此處省略非關鍵代碼

服務端處理

當 Nacos 服務端接收到來自客戶端的 gRPC 調用請求后,會立即啟動一系列處理流程,以確保請求能夠得到有效響應。關鍵代碼的實現細節可以參考下面這部分。

@Override
@TpsControl(pointName = "RemoteNamingServiceSubscribeUnSubscribe", name = "RemoteNamingServiceSubscribeUnsubscribe")
@Secured(action = ActionTypes.READ)
@ExtractorManager.Extractor(rpcExtractor = SubscribeServiceRequestParamExtractor.class)
public SubscribeServiceResponse handle(SubscribeServiceRequest request, RequestMeta meta) throws NacosException {
    String namespaceId = request.getNamespace();
    String serviceName = request.getServiceName();
    String groupName = request.getGroupName();
    String app = RequestContextHolder.getContext().getBasicContext().getApp();
    String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
    Service service = Service.newService(namespaceId, groupName, serviceName, true);
    Subscriber subscriber = new Subscriber(meta.getClientIp(), meta.getClientVersion(), app, meta.getClientIp(),
            namespaceId, groupedServiceName, 0, request.getClusters());
    ServiceInfo serviceInfo = ServiceUtil.selectInstancesWithHealthyProtection(serviceStorage.getData(service),
            metadataManager.getServiceMetadata(service).orElse(null), subscriber.getCluster(), false, true,
            subscriber.getIp());
    if (request.isSubscribe()) {
        clientOperationService.subscribeService(service, subscriber, meta.getConnectionId());
        NotifyCenter.publishEvent(new SubscribeServiceTraceEvent(System.currentTimeMillis(),
                NamingRequestUtil.getSourceIpForGrpcRequest(meta), service.getNamespace(), service.getGroup(),
                service.getName()));
    } else {
        clientOperationService.unsubscribeService(service, subscriber, meta.getConnectionId());
        NotifyCenter.publishEvent(new UnsubscribeServiceTraceEvent(System.currentTimeMillis(),
                NamingRequestUtil.getSourceIpForGrpcRequest(meta), service.getNamespace(), service.getGroup(),
                service.getName()));
    }
    return new SubscribeServiceResponse(ResponseCode.SUCCESS.getCode(), "success", serviceInfo);
}

這段代碼包括提取請求信息、創建相關對象、處理訂閱或取消訂閱的操作,并返回相應的結果。通過這種方式,Nacos 可以高效管理微服務的服務發現和注冊功能。

心跳監測

在 Nacos 2.1 版本之前,每個服務在運行時都會向注冊中心發送一次請求,以通知其當前的存活狀態和正常性。這種機制雖然有效,但在高并發環境下可能會引入額外的網絡負擔和延遲。

然而,升級到 2.1 版本后,這一過程發生了顯著的變化。首先,我們需要思考一下心跳監測的本質。顯然,心跳監測是一種定期檢查機制,這意味著服務會在設定的時間間隔內自動發送心跳信號以確認其存活狀態。因此,可以合理地推測,這一功能在客戶端實現為一個定時任務,它會按照預定的時間頻率定期向注冊中心報告服務的健康狀態。

為了更好地理解這一機制的實現,我們接下來將重點關注相關的關鍵代碼。

public final void start() throws NacosException {
       // 省略一些代碼
        
        clientEventExecutor = new ScheduledThreadPoolExecutor(2, r -> {
            Thread t = new Thread(r);
            t.setName("com.alibaba.nacos.client.remote.worker");
            t.setDaemon(true);
            return t;
        });
        
        // 省略一些代碼
        
        clientEventExecutor.submit(() -> {
            while (true) {
                try {
                    if (isShutdown()) {
                        break;
                    }
                    ReconnectContext reconnectContext = reconnectionSignal
                            .poll(keepAliveTime, TimeUnit.MILLISECONDS);
                    if (reconnectContext == null) {
                        // check alive time.
                        if (System.currentTimeMillis() - lastActiveTimeStamp >= keepAliveTime) {
                            boolean isHealthy = healthCheck();
                            if (!isHealthy) {
                                 // 省略一些代碼

我將與健康監測無關的代碼基本去除了,這樣你可以更加直觀地觀察 Nacos 是如何進行實例健康監測的。由于健康監測的核心目的在于確認服務的可用性,因此這一過程的實現相對簡單。

在這段代碼中,我們可以清晰地看到,健康監測并不涉及任何復雜的數據傳輸。其主要功能僅僅是向服務器發送請求,以檢測服務器是否能夠成功響應。這種設計極大地降低了網絡開銷,使得監測過程更加高效。

圖片圖片

服務端的代碼同樣清晰且簡單。如下所示:

@Override
@TpsControl(pointName = "HealthCheck")
public HealthCheckResponse handle(HealthCheckRequest request, RequestMeta meta) {
    return new HealthCheckResponse();
}

總體而言,這種優化顯著減少了網絡 I/O 的消耗,提升了系統的整體性能。乍一看,似乎并沒有做什么復雜的操作,但這并不意味著我們就無法判斷客戶端是否能夠正常連接。實際上,關鍵的判斷邏輯被設計在外層代碼中。

Connection connection = connectionManager.getConnection(GrpcServerConstants.CONTEXT_KEY_CONN_ID.get());
RequestMeta requestMeta = new RequestMeta();
requestMeta.setClientIp(connection.getMetaInfo().getClientIp());
requestMeta.setConnectionId(GrpcServerConstants.CONTEXT_KEY_CONN_ID.get());
requestMeta.setClientVersion(connection.getMetaInfo().getVersion());
requestMeta.setLabels(connection.getMetaInfo().getLabels());
requestMeta.setAbilityTable(connection.getAbilityTable());
//這里刷新下時間。用來代表它確實存活
connectionManager.refreshActiveTime(requestMeta.getConnectionId());
prepareRequestContext(request, requestMeta, connection);
//這次處理的返回
Response response = requestHandler.handleRequest(request, requestMeta);

別著急,服務端同樣運行著一個定時任務,負責定期掃描和檢查各個客戶端的狀態。我們看下:

public void start() {
    initConnectionEjector();
    // Start UnHealthy Connection Expel Task.
    RpcScheduledExecutor.COMMON_SERVER_EXECUTOR.scheduleWithFixedDelay(() -> {
        runtimeConnectionEjector.doEject();
        MetricsMonitor.getLongConnectionMonitor().set(connections.size());
    }, 1000L, 3000L, TimeUnit.MILLISECONDS);
//省略部分代碼,doEject方法再往后走,你就會發現這樣一段代碼
//outdated connections collect.
for (Map.Entry<String, Connection> entry : connections.entrySet()) {
    Connection client = entry.getValue();
    if (now - client.getMetaInfo().getLastActiveTime() >= KEEP_ALIVE_TIME) {
        outDatedConnections.add(client.getMetaInfo().getConnectionId());
    } else if (client.getMetaInfo().pushQueueBlockTimesLastOver(300 * 1000)) {
        outDatedConnections.add(client.getMetaInfo().getConnectionId());
    }
}
//省略部分代碼,

通過這些分析,你基本上已經掌握了核心概念和實現細節。我們不需要再多做贅述。我們繼續往下看。

負載均衡

談到負載均衡,首先我們需要確保本地擁有一份服務器列表,以便于合理地分配負載。因此,關鍵在于我們如何從注冊中心獲取這些可用服務的信息。那么,具體來說,我們應該如何在本地有效地發現和獲取這些服務呢?

服務發現

服務發現的機制會隨著實例的增加或減少而動態變化,因此我們需要定期更新可用服務列表。這就引出了一個重要的設計考量:為什么不將服務發現的檢索任務直接整合到心跳任務中呢?

首先,心跳任務的主要目的是監測服務實例的健康狀態,確保它們能夠正常響應請求。而服務發現則側重于及時更新和獲取當前可用的服務實例信息。這兩者的目的明顯不同,因此將它們混合在一起可能會導致邏輯上的混淆和功能上的復雜性。

此外,兩者的時間間隔也各有不同。心跳監測可能需要更頻繁地進行,以及時發現和處理服務故障,而服務發現的頻率可以根據具體需求適當調整。基于這些原因,將心跳監測和服務發現分開成兩個獨立的定時任務,顯然是更合理的選擇。

接下來,讓我們深入研究服務發現的關鍵代碼,看看具體是如何實現這一機制的:

public void run() {
    //省略部分代碼
    if (serviceObj == null) {
        serviceObj = namingClientProxy.queryInstancesOfService(serviceName, groupName, clusters, 0, false);
        serviceInfoHolder.processServiceInfo(serviceObj);
        lastRefTime = serviceObj.getLastRefTime();
        return;
    }
    
    if (serviceObj.getLastRefTime() <= lastRefTime) {
        serviceObj = namingClientProxy.queryInstancesOfService(serviceName, groupName, clusters, 0, false);
        serviceInfoHolder.processServiceInfo(serviceObj);
    }
    //省略部分代碼

當然,接下來我們將探討服務器端的處理邏輯,以下是服務端處理的關鍵代碼部分:

public QueryServiceResponse handle(ServiceQueryRequest request, RequestMeta meta) throws NacosException {
    String namespaceId = request.getNamespace();
    String groupName = request.getGroupName();
    String serviceName = request.getServiceName();
    Service service = Service.newService(namespaceId, groupName, serviceName);
    String cluster = null == request.getCluster() ? "" : request.getCluster();
    boolean healthyOnly = request.isHealthyOnly();
    ServiceInfo result = serviceStorage.getData(service);
    ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(null);
    result = ServiceUtil.selectInstancesWithHealthyProtection(result, serviceMetadata, cluster, healthyOnly, true,
            NamingRequestUtil.getSourceIpForGrpcRequest(meta));
    return QueryServiceResponse.buildSuccessResponse(result);
}

這樣一來,我們便能夠獲得一些關鍵的服務信息。

負載均衡算法

如果同一個微服務存在多個 IP 地址,那么在進行服務調用時,我們該如何選擇具體的服務器呢?通常,我們會想到使用 Nginx 作為服務端的負載均衡工具。然而,除了在服務器端進行負載均衡之外,我們同樣可以在微服務客戶端配置負載算法,以優化請求的分發。

此時,我們要明確的是,這部分邏輯實際上并不屬于 Nacos 的職責范圍,而是由另一個組件——Ribbon 來負責。Ribbon 專注于實現客戶端負載均衡,確保在微服務架構中,客戶端能夠智能地選擇合適的服務器進行調用,從而提高系統的性能和穩定性。

接下來,我們可以深入查看 Ribbon 的關鍵代碼,了解它是如何選擇服務器的。,具體來說,Ribbon 通過一個名為 LoadBalance 的類來攔截請求,并根據預設的負載均衡策略來挑選合適的服務器。

圖片圖片

讓我們來深入分析一下關鍵代碼,其實所有的負載均衡算法邏輯都集中在 getServer 方法的實現中。

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
        throws IOException {
    ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
    Server server = getServer(loadBalancer, hint);
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    }
    RibbonServer ribbonServer = new RibbonServer(serviceId, server,
            isSecure(server, serviceId),
            serverIntrospector(serviceId).getMetadata(server));

    return execute(serviceId, ribbonServer, request);
}

我們可以對負載均衡策略進行局部配置,以便根據特定的業務需求和場景靈活調整服務調用的行為。

#被調用的微服務名
mall‐order:
 ribbon:
    #指定使用Nacos提供的負載均衡策略(優先調用同一集群的實例,基于隨機&權重)
    NFLoadBalancerRuleClassName:com.alibaba.cloud.nacos.ribbon.NacosRule

當然,我們也可以進行全局配置,以便在整個系統范圍內統一管理負載均衡策略和參數。

@Bean
public IRule ribbonRule() {
    // 指定使用Nacos提供的負載均衡策略(優先調用同一集群的實例,基于隨機權重)
    return new NacosRule();
}

總結

隨著本文的深入探討,我們對微服務架構中的服務發現與注冊機制有了更全面的認識。從單體架構的局限性到微服務的靈活性,我們見證了架構演進的歷程。服務發現與注冊作為微服務通信的基石,其重要性不言而喻。通過Nacos這一強大的注冊中心,我們不僅實現了服務的動態注冊與發現,還通過心跳監測、負載均衡等機制,確保了服務的高可用性和穩定性。

在技術選型上,Nacos的gRPC實現展示了其在性能優化方面的潛力,同時也帶來了系統復雜性的挑戰。然而,通過精心設計的客戶端和服務端代碼,我們能夠有效地管理服務實例,實現服務的快速響應和負載均衡。這些機制的實現,不僅提升了系統的伸縮性和容錯性,也為微服務的快速發展提供了堅實的基礎。

責任編輯:武曉燕 來源: 靈墨AI探索室
相關推薦

2019-08-05 10:00:13

LinuxBash命令

2020-05-07 18:20:52

Git腳本Linux開源

2013-09-18 09:40:32

企業BYOD企業應用商店

2025-03-31 08:35:00

Eureka微服務架構

2021-11-30 05:51:46

React開發工具

2020-11-09 06:51:46

開源工具開源

2012-04-18 17:06:41

PhoneGap

2014-01-09 14:25:19

MacOS X工具

2017-03-27 17:53:45

Linux

2011-02-22 08:55:42

Chrome企業瀏覽器

2013-01-04 09:53:32

大數據技術大數據

2015-05-07 13:38:15

2014-03-03 11:02:35

開放網絡SDN博科

2022-03-29 10:03:12

IT領導者首席信息官

2023-10-06 12:47:35

模型訓練

2022-11-08 08:49:09

IT專家職業要素

2009-07-08 14:24:43

Java日志系統跟蹤調試

2024-10-17 16:01:02

2023-05-04 12:37:24

點贊
收藏

51CTO技術棧公眾號

欧美一区亚洲一区| 日韩精品视频免费专区在线播放 | 亚洲一区二区中文在线| 久久超碰亚洲| 国产精品久久久久久久久久久久久久久久久久| 欧美精品1区| 亚洲天堂av在线免费观看| 91网址在线观看精品| 麻豆蜜桃在线观看| 欧美极品aⅴ影院| 99在线视频首页| 黄色av一区二区| 最新成人av网站| xxxx欧美18另类的高清| 久久无码人妻精品一区二区三区| 久久久久久久性潮| 欧美视频中文在线看| 青青草影院在线观看| 日韩精品系列| 丁香六月久久综合狠狠色| 国产日韩欧美视频| 精产国品一区二区| 99精品视频免费| 欧美精品亚州精品| 综合 欧美 亚洲日本| 日韩av三区| 精品久久久久久久久久久久久久久久久| 日韩中文字幕二区| 岛国在线视频网站| 亚洲另类中文字| 亚洲精品一品区二品区三品区| 日本wwwxxxx| 国产精品亚洲一区二区三区妖精| 国产精品永久在线| 国产又粗又猛又爽又| 亚洲欧美高清| 97久久国产精品| 国产污片在线观看| 国模一区二区三区| 欧美日韩高清区| 丝袜 亚洲 另类 欧美 重口| 色综合天天爱| 色系列之999| 欧美亚洲色综久久精品国产| 久久97视频| 亚洲美女久久久| 国产精品麻豆入口| 欧美一级二级三级视频| 亚洲成人免费网站| 9.1在线观看免费| 日韩精品一区二区三区中文在线| 欧美日韩国产另类一区| 免费看国产黄色片| 日韩一区二区三区免费视频| 欧美亚州韩日在线看免费版国语版| 成人在线看视频| 亚洲欧美在线成人| 欧美吞精做爰啪啪高潮| 婷婷免费在线观看| www欧美在线观看| 日韩视频免费观看高清完整版在线观看 | 国产美女高潮在线观看| 精品久久中文字幕| 免费av网址在线| 欧美日韩精品一区二区三区视频| 在线观看av一区| 国产成人美女视频| 欧美电影院免费观看| 亚洲成色777777女色窝| 精品黑人一区二区三区观看时间| 久久综合亚洲| 久久精品国产亚洲| 欧美激情国产精品免费| 国产精品一级| 国产精品欧美激情在线播放| 91国内精品久久久| 懂色av一区二区三区免费观看| 好吊色欧美一区二区三区视频 | 日韩高清欧美| 国产激情一区二区三区桃花岛亚洲| 午夜精品福利视频| www.国产毛片| 狠狠色狠狠色综合| 国产一级特黄a大片99| 深夜福利在线视频| 国产精品国产三级国产三级人妇 | 日韩国产欧美在线播放| 国产精品亚洲片夜色在线| 精品国产va久久久久久久| 成人av片在线观看| 一区二区三区|亚洲午夜| 欧美大胆的人体xxxx| 91国产免费观看| 日韩高清在线一区二区| 亚洲精品播放| 九九精品在线播放| 日本视频免费观看| 国产成人免费视频精品含羞草妖精| 久久综合九九| 1区2区3区在线视频| 色综合久久综合网| 波多野结衣三级视频| 国产精品免费大片| 欧美黑人xxxⅹ高潮交| 涩涩视频在线观看| www.欧美.com| 黄色一级视频播放| 国产一区一一区高清不卡| 日韩免费看网站| 精品一区二区6| 99热这里只有成人精品国产| 成人午夜在线观看| 精品无人乱码| 亚洲超碰精品一区二区| 超碰成人在线播放| 国产伦精品一区二区三区视频| 久久99亚洲热视| 在线观看视频中文字幕| 久久亚洲精精品中文字幕早川悠里 | 国产污视频在线| 性感美女久久精品| 超碰人人cao| 999精品在线| 国产精品免费福利| 青青草在线免费视频| 亚洲成av人片观看| 国产香蕉精品视频| 午夜久久黄色| 91精品啪在线观看麻豆免费| 9191在线| 欧美性大战久久| 久久成人激情视频| 久久福利影视| 麻豆亚洲一区| 日本不卡免费高清视频在线| 精品99999| 久久精品女人毛片国产| 福利电影一区二区| 久草视频这里只有精品| 亚洲一区二区三区免费| 久久99精品久久久久久琪琪| 99国产在线播放| 亚洲女爱视频在线| 一级黄色片在线免费观看| 99久久久久国产精品| 国产精品自在线| 欧美人xxx| 欧美一区二区三区四区久久 | 亚洲精品久久久| 成人精品视频99在线观看免费| 在线观看的av| 欧美日韩极品在线观看一区| 亚洲一级二级片| 国产大片一区二区| av日韩一区二区三区| 日本一道高清一区二区三区| 欧洲成人免费aa| 国产一二三区在线视频| 欧美视频在线观看一区| 永久免费看片直接| 国产二区国产一区在线观看| 日韩 欧美 视频| 欧美jizz19性欧美| 国产国语videosex另类| eeuss影院www在线观看| 欧美久久久久久久久久| 久久亚洲成人av| 99久久伊人网影院| 久久综合久久色| 日韩欧美视频在线播放| 92福利视频午夜1000合集在线观看| 在线黄色网页| 精品丝袜一区二区三区| 中文字幕一区二区久久人妻| 亚洲男人天堂av网| 妖精视频一区二区| 日韩在线a电影| 妞干网这里只有精品| 韩国精品福利一区二区三区| 国产成人免费91av在线| a视频在线免费看| 亚洲精品91美女久久久久久久| 无码人妻丰满熟妇区bbbbxxxx| 中文字幕中文乱码欧美一区二区| 中文字幕制服丝袜| 日韩精品免费专区| 免费看污污视频| 久久不见久久见国语| 3d动漫精品啪啪一区二区三区免费| 九色porny视频在线观看| 中文字幕综合一区| 日韩中文字幕免费在线观看| 欧美少妇性性性| 日本熟妇色xxxxx日本免费看| 国产欧美1区2区3区| 国产乱淫av麻豆国产免费| 日韩综合小视频| 九九热只有这里有精品| 欧美综合视频| 精品一区国产| 精品久久亚洲| 国产精品美女主播| 天堂中文在线播放| 欧美成人免费网| 国产高清视频在线播放| 亚洲第一偷拍网| 国产精品毛片一区二区在线看舒淇 | 天天爱天天做天天操| 天天躁日日躁狠狠躁欧美| 91免费在线观看网站| 电影一区二区| 97人人模人人爽人人喊中文字| 黄色网在线免费看| 国产一区二区三区丝袜| 婷婷在线免费观看| 欧美一区二区性放荡片| 欧美一级黄视频| 欧美性猛交xxxx| 国产午夜福利一区二区| 亚洲另类色综合网站| 亚洲色图欧美色| 久久久美女毛片| 国产一级伦理片| 国产成人免费av在线| 久久成年人网站| 蜜臀av一区二区三区| 欧美激情精品久久久久久小说| 亚洲精品影视| 免费av手机在线观看| 欧美破处大片在线视频| 熟女视频一区二区三区| 欧美r级电影| 一区二区三区在线观看www| 国内精品视频在线观看| 久久综合中文色婷婷| 一本久久青青| 欧美日韩精品综合| 最新国产一区| 欧美黄色直播| 国产一区网站| 日韩欧美一区二区在线观看 | 自拍偷拍福利视频| 在线精品亚洲一区二区不卡| 波多野结衣电车| 日本道精品一区二区三区| 怡红院av久久久久久久| 在线观看成人小视频| 亚洲午夜无码久久久久| 欧美午夜在线一二页| 中文字幕网址在线| 欧美一区欧美二区| 亚洲AV无码精品国产| 精品欧美一区二区在线观看| 黑人精品一区二区三区| 亚洲国产欧美一区二区三区久久| 午夜国产在线视频| 亚洲视频电影图片偷拍一区| a视频网址在线观看| 久久久999精品| 丝袜在线观看| 欧美在线亚洲在线| 成人看片毛片免费播放器| 91精品久久久久久久久久久| 国产精品一区二区三区四区在线观看| 99爱精品视频| 亚洲涩涩av| 亚洲欧美电影在线观看| 自拍偷拍欧美| 国产资源在线视频| 轻轻草成人在线| 伊人成人免费视频| 99视频有精品| 久久精品色妇熟妇丰满人妻| 亚洲精品免费播放| 国产又黄又爽又色| 欧美网站一区二区| 欧美一级淫片aaaaaa| 亚洲香蕉成人av网站在线观看| 黄色动漫在线| 欧美在线精品免播放器视频| 日韩一区二区三区四区五区 | 国产欧美精品va在线观看| 电影中文字幕一区二区| 玛丽玛丽电影原版免费观看1977 | 亚洲一区成人在线| 潘金莲一级淫片aaaaaa播放| 欧美一区二区在线观看| 免费av在线电影| 欧美国产日韩视频| 精品123区| 精品一卡二卡三卡四卡日本乱码 | 亚洲精华一区二区三区| youjizz.com亚洲| 国产精品久久久久9999高清| 国产精品自在自线| 久久综合精品国产一区二区三区| 黄色a级片在线观看| 一本久道中文字幕精品亚洲嫩 | 日韩成人免费视频| 精品国产白色丝袜高跟鞋| 欧美重口另类videos人妖| 九九九九九九精品任你躁| 欧美日韩综合久久| 国产一区久久| 男女污污视频网站| 久久久亚洲高清| 日本午夜精品理论片a级app发布| 欧美日韩国产综合一区二区三区 | 久久久精品2019中文字幕之3| 91在线播放观看| 欧美视频日韩视频在线观看| 四虎在线观看| 国内精品久久久久| 久久久精品区| 在线观看一区二区三区三州| 久久久亚洲一区| 捆绑裸体绳奴bdsm亚洲| 一区二区三区鲁丝不卡| 97超碰人人草| 综合网中文字幕| 在线观看欧美日韩电影| 精品国产二区在线| 欧美午夜久久| 天天操夜夜操很很操| 国产精品亲子乱子伦xxxx裸| 中文字幕 国产精品| 国产婷婷97碰碰久久人人蜜臀| av手机在线观看| 国产精品theporn88| 欧美暴力喷水在线| 青青草原播放器| 国产精品免费视频一区| 天天爱天天做天天爽| 亚洲欧美在线免费观看| 成人香蕉视频| 日本一区二区三区免费看 | 亚洲网中文字幕| 国产精品第一页第二页第三页| 中文字幕男人天堂| 中文字幕日韩欧美| 久久久久伊人| 日本不卡一区二区三区四区| 激情综合色播激情啊| 秋霞欧美一区二区三区视频免费| 欧美男女性生活在线直播观看| 欧美性天天影视| 91免费电影网站| 欧美日韩一卡| 50一60岁老妇女毛片| 日韩欧美在线网址| 巨骚激情综合| 国产精品稀缺呦系列在线| 99久久综合| 91人人澡人人爽| 午夜精品久久久久久久99樱桃| 天天在线女人的天堂视频| 国产精品对白刺激| 羞羞色午夜精品一区二区三区| 天天综合成人网| 夜夜精品视频一区二区| 污污视频在线观看网站| 全亚洲最色的网站在线观看| 色天天久久综合婷婷女18| 伊人成人免费视频| 欧美日韩激情小视频| а√天堂中文在线资源bt在线| 成人春色激情网| 尤物在线精品| 国产7777777| 日韩欧美成人激情| 都市激情亚洲综合| 亚洲图片都市激情| 国产成人免费xxxxxxxx| 视频一区二区三区四区五区| 日韩在线一区二区三区免费视频| 久久免费精品| 精品国产成人av在线免| 亚洲男人的天堂在线观看| 手机在线不卡av| 国产精品爽爽爽| 在线观看一区| 国产日韩精品中文字无码| 欧美成人一区二区三区| 都市激情亚洲一区| 国产精品视频一二三四区| 久久嫩草精品久久久久| 国产日韩在线观看一区| 欧洲一区二区视频| 欧美1区2区| 亚洲AV无码片久久精品| 日韩欧美国产wwwww| 91看片一区| 成人免费播放器| 国产精品久久久久久久裸模| 手机在线观看毛片| 亚洲影视中文字幕| 毛片基地黄久久久久久天堂| 国产视频91在线| 久久精品影视伊人网| 嫩草影视亚洲|