spring cloud loadbalancer實現機房感知的負載均衡

1 概述

在同城多機房情景下,各個機房各自部署一套微服務集群,正常情況下微服務調用在本機房閉環。在如下某些災難情景,可以嘗試拉遠調用以最大程度維持業務連續性,這些情景例如:

  • A機房多個服務器宕機。
  • 應用由于BUG發生OOM導致暫時性應用不可用、或者被kubelet重啟,等應用重新正常運行需要5分鐘以上。

為了實現拉遠調用,進程的負載均衡邏輯需要感知機房位置,因此微服務注冊到服務注冊中心時需要夾帶額外的元數據。

2 spring cloud loadbalancer

Spring Cloud LoadBalancer是Spring Cloud提供的一個用于微服務架構中的客戶端負載均衡解決方案。它旨在取代Netflix Ribbon,提供了更現代化的API和更好的與Spring生態系統的集成。

2.1 主要特性

  • 簡化配置:
    Spring Cloud LoadBalancer提供了簡化的配置選項,并且可以通過應用程序屬性文件輕松配置。
  • 自動配置支持:
    它能夠自動與RestTemplate和Feign客戶端集成,無需手動設置負載均衡邏輯。
  • 反應式編程支持:
    支持基于 WebFlux 的非阻塞 I/O 操作,對于構建高性能、響應式的微服務非常重要。
  • 靈活的負載均衡策略:
    內置多種負載均衡算法(如輪詢、隨機選擇等),并且可以自定義實現以滿足特定需求。
  • 服務發現集成:
    與Spring Cloud DiscoveryClient接口兼容,可以與Eureka、Consul等服務發現工具無縫協作。

2.2 自定義負載均衡的套路

2.2.1 步驟1

編寫自定義負載均衡邏輯的類,內容如下:

package com.example.consumer.balancer;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import  org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import  org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;public class MyNewLoadBalancer implements ReactorServiceInstanceLoadBalancer {private static final Log log =  LogFactory.getLog(MyNewLoadBalancer.class);private final String serviceId;private ObjectProvider<ServiceInstanceListSupplier>  serviceInstanceListSupplierProvider;private final String localDataCenter;/**
*      * @param serviceInstanceListSupplierProvider a provider of
*             * {@link ServiceInstanceListSupplier} that will be used to get available instances
*                    * @param serviceId id of the service for which to choose an  instance
*                           */public MyNewLoadBalancer(ObjectProvider<ServiceInstanceListSupplier>  serviceInstanceListSupplierProvider,String serviceId, String localDataCenter) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider =  serviceInstanceListSupplierProvider;this.localDataCenter = localDataCenter;}@SuppressWarnings("rawtypes")@Override// 核心方法,負載均衡的邏輯就是從choose()開始public Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier =  serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map(serviceInstances ->  processInstanceResponse(supplier, serviceInstances));}private Response<ServiceInstance>  processInstanceResponse(ServiceInstanceListSupplier supplier,List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse =  getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback &&  serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback)  supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance>  getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " +  serviceId);}return new EmptyResponse();}// 同機房的服務實例List<ServiceInstance> sameDcInstances = instances.stream().filter(instance -> localDataCenter.equals(instance.getMetadata().get("DATA_CENTER"))).collect(Collectors.toList());// 其他機房的服務實例List<ServiceInstance> otherDcInstances = instances.stream().filter(instance -> !localDataCenter.equals(instance.getMetadata().get("DATA_CENTER"))).collect(Collectors.toList());// 兩個服務實例列表,選擇一個  List<ServiceInstance> selectedInstances = sameDcInstances.isEmpty() ?otherDcInstances : sameDcInstances;// 選好實例列表后,再使用隨機方式挑選出一個int index =  ThreadLocalRandom.current().nextInt(selectedInstances.size());ServiceInstance instance = selectedInstances.get(index);return new DefaultResponse(instance);}
}

2.2.2 步驟2

編寫工廠類,不需要添加@Configuration:

package com.example.consumer.balancer;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;public class MyLoadBalancerConfig {@Beanpublic ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment  environment, LoadBalancerClientFactory loadBalancerClientFactory){String name =  environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);// 本地機房的信息,從環境變量中獲取即可String localDataCenter = environment.getProperty("spring.cloud.nacos.discovery.metadata.DATA_CENTER");return new MyNewLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,  ServiceInstanceListSupplier.class), name, localDataCenter);}
}

2.2.3 步驟3

在main類中使用@LoadBalancerClient或@LoadBalancerClients來指定剛剛創建工廠類:

package com.example.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import com.example.consumer.balancer.MyLoadBalancerConfig;@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@LoadBalancerClient(name = "service-provider", configuration =  MyLoadBalancerConfig.class)
// @LoadBalancerClients(defaultConfiguration = MyLoadBalancerConfig.class)
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}
}

2.2.4 完整代碼

https://gitee.com/handsomeboylj/spring-cloud-nacos-demo

3 容災方案

兩邊機房都正常時:
在這里插入圖片描述
DC1機房的Provider應用臨時不可用時,拉遠調用另外機房的Provider應用:
在這里插入圖片描述

4 測試

本次測試中,namespace dc1作為dc1機房,namespace dc2作為dc2機房,所有微服務實例都注冊到同一個nacos服務中,所有微服務實例在網絡層都是扁平的、可直接調用的(對應到現實里,就是是兩個機房通過VPN或專線打通,容器網絡使用underlay模式)。

git clone https://gitee.com/handsomeboylj/spring-cloud-nacos-demo.git
kubectl apply -f doc/k8s/dc-awareness/

部署成功后,如下:
在這里插入圖片描述
dc1機房的一個消費者的IP是10.0.13.96,其工作端口是8082,接口是/consumer/call,調用可以看見結果,消費者和生產者都會響應自己所在的機房:
在這里插入圖片描述
將dc1機房的生產者關閉后,再訪問dc1機房的消費者的接口,可以看見響應是dc2,說明調用了機房2的生產者。
在這里插入圖片描述

將dc1機房的生產者重新上線后,dc1的消費者從拉遠調用轉變成本機房調用
在這里插入圖片描述

5 小結

本文介紹拉遠調用可臨時維持業務系統的連續性,并且使用spring cloud loadbalancer來實現感知機房,優先本機房閉環調用,次之拉遠調用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83067.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83067.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83067.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue中,created和mounted兩個鉤子之間調用時差值受什么影響

在 Vue 中&#xff0c;created 和 mounted 是兩個生命周期鉤子&#xff0c;它們之間的調用時差主要受以下幾個因素影響&#xff1a; &#x1f7e2; 1. 模板復雜度與渲染耗時&#xff08;最主要因素&#xff09; mounted 的觸發時間是在組件的 DOM 被掛載之后&#xff08;也就是…

Linux篇 第2章Linux基礎指令

Linux篇 第2章Linux基礎指令 文章目錄 前言一、基礎的一些命令1.pwd2.mkdir3.ls4.cd5.clear 二、ls1.ls -l2.ls -a3.ls -l -a 三、touch四、 cd1.cd /2.cd ..3.cd ~4. cd - 五、tree1. Linux系統文件的結構2.絕對路徑和相對路徑 六、mkdir -p七、rmdir&#xff08;沒啥用&#…

Scrapyd 詳解:分布式爬蟲部署與管理利器

Scrapyd 是 Scrapy 官方提供的爬蟲部署與管理平臺&#xff0c;支持分布式爬蟲部署、定時任務調度、遠程管理爬蟲等功能。本文將深入講解 Scrapyd 的核心功能、安裝配置、爬蟲部署流程、API 接口使用&#xff0c;以及如何結合 Scrapy-Redis 實現分布式爬蟲管理。通過本文&#x…

國產免費工作流引擎star 6.5k,Warm-Flow升級1.7.2(新增案例和修復缺陷)

文章目錄 主要更新內容項目介紹功能思維導圖設計器流程圖演示地址官網Warm-Flow視頻 主要更新內容 [feat] 開啟流程實例&#xff0c;新增流程定義是否存在校驗[feat] 新增合同簽訂流程案例[feat] 新增企業采購流程案例[update] mybatis-plus邏輯刪除&#xff0c;刪除值和未刪除…

數據倉庫Hive

1.數據倉庫 1.1數據倉庫的概念 數據倉庫&#xff08;Data Warehouse&#xff09;是一個面向主題的、集成的、相對穩定的、反映歷史變化的數據集合&#xff0c;用于支持管理決策。 面向主題。操作型數據庫的數據組織面向事務處理任務&#xff0c;而數據倉庫中的數據按照一定的…

dify 連接不上ollama An error occurred during credentials validation:

三大報錯 An error occurred during credentials validation: HTTPConnectionPool(hosthost.docker.internal, port11434): Max retries exceeded with url: /api/chat (Caused by NameResolutionError("<urllib3.connection.HTTPConnection object at 0x7f26fc3c00b0&…

uniapp 生成海報二維碼 (微信小程序)

先下載qrcodenpm install qrcode 調用 community_poster.vue <template><view class"poster-page"><uv-navbar title"物業推廣碼" placeholder autoBack></uv-navbar><view class"community-info"><text clas…

如何理解編程中的遞歸、迭代與回歸?

作為編程初學者&#xff0c;遞歸、迭代和回歸這三個概念常常讓人感到困惑。本文將通過生活化的比喻、Python代碼示例和直觀的對比&#xff0c;幫助你徹底理解這三個重要概念及其應用場景。 一、從生活比喻理解核心概念 1. 遞歸&#xff08;Recursion&#xff09;—— 俄羅斯套…

Android Studio 模擬器配置方案

Android Studio 模擬器配置方案 1.引言2.使用Android Studio中的模擬器3.使用國產模擬器1.引言 前面介紹【React Native基礎環境配置】的時候需要配置模擬器,當時直接使用了USB調試方案,但是有些時候可能不太方便連接手機調試,比如沒有iPhone調不了ios。接下來說明另外兩種可…

uniapp(vue3)動態計算swiper高度封裝自定義hook

// useCalculateSwiperHeight.ts import { ref, onMounted } from vue;export function useCalculateSwiperHeight(headerSelector: string .header-search, tabsWrapperSelector: string .u-tabs .u-tabs__wrapper) {const swiperHeight ref<number>(0);// 封裝uni.g…

從代碼學習深度學習 - 轉置卷積 PyTorch版

文章目錄 前言基本操作填充、步幅和多通道填充 (Padding)步幅 (Stride)多通道總結前言 在卷積神經網絡(CNN)的大家族中,我們熟悉的卷積層和匯聚(池化)層通常會降低輸入特征圖的空間維度(高度和寬度)。然而,在許多應用場景中,例如圖像的語義分割(需要對每個像素進行分…

c語言第一個小游戲:貪吃蛇小游戲06

實現貪吃蛇四方向的風騷走位 實現代碼 #include <curses.h> #include <stdlib.h> struct snake{ int hang; int lie; struct snake *next; }; struct snake *head; struct snake *tail; int key; int dir; //全局變量 #define UP 1 //這個是宏定義&a…

django的權限角色管理(RBAC)

在 Django 中&#xff0c;User、Group 和 Permission 是權限系統的核心組件。下面通過代碼示例演示它們的 CRUD&#xff08;創建、讀取、更新、刪除&#xff09; 操作&#xff1a; 一、User 模型 CRUD from django.contrib.auth.models import User# 創建用戶 user User.obje…

解決docker alpine缺少字體的問題 Could not initialize class sun.awt.X11FontManager

制作的springboot項目鏡像&#xff0c;缺少字體報錯Could not initialize class sun.awt.X11FontManager 原因鏡像中缺少字體 解決&#xff1a; 制作鏡像時&#xff0c;添加字體庫&#xff0c;Dockerfile文件 中添加如下內容 注意&#xff1a; jdk版本一定要使用&#xff0…

MQTT 在Spring Boot 中的使用

在 Spring Boot 中使用 MQTT 通常會借助 Spring Integration 項目提供的 MQTT 支持。這使得 MQTT 的集成可以很好地融入 Spring 的消息驅動和企業集成模式。 以下是如何在 Spring Boot 中集成和使用 MQTT 的詳細步驟&#xff1a; 前提條件&#xff1a; MQTT Broker&#xff…

養生:為健康生活注入活力

在快節奏的現代生活中&#xff0c;養生不再是老年人的專屬&#xff0c;而是每個人維持身心健康的必修課。從飲食到運動&#xff0c;從睡眠到心態&#xff0c;全方位的養生方式能幫助我們抵御壓力&#xff0c;擁抱充滿活力的生活。 飲食養生&#xff1a;合理搭配&#xff0c;滋…

Axure設計之內聯框架切換頁面、子頁面間跳轉問題

在Axure中&#xff0c;你可以通過以下步驟實現主頁面中的內聯框架在點擊按鈕時切換頁面內容&#xff0c;從A頁面切換到B頁面。&#xff08;誤區&#xff1a;子頁面之間切換不要設置“框架中打開鏈接”然后選“父級框架”這個交互&#xff09; 主框架頁面&#xff08;左側導航展…

[思維模式-38]:看透事物的關系:什么是事物的關系?事物之間的關系的種類?什么是因果關系?如何通過數學的方式表達因果關系?

一、什么是事物的關系&#xff1f; 事物的關系是指不同事物之間存在的各種聯系和相互作用&#xff0c;它反映了事物之間的相互依存、相互影響、相互制約等特性。以下從不同維度為你詳細闡述&#xff1a; 1、關系的類型 因果關系 定義&#xff1a;一個事件&#xff08;原因&a…

OJ判題系統第6期之判題邏輯開發——設計思路、實現步驟、代碼實現(策略模式)

在看這期之前&#xff0c;建議先看前五期&#xff1a; Java 原生實現代碼沙箱&#xff08;OJ判題系統第1期&#xff09;——設計思路、實現步驟、代碼實現-CSDN博客 Java 原生實現代碼沙箱之Java 程序安全控制&#xff08;OJ判題系統第2期&#xff09;——設計思路、實現步驟…

行業趨勢與技術創新:駕馭工業元宇宙與綠色智能制造

引言 制造業發展的新格局&#xff1a;創新勢在必行 當今制造業正經歷深刻變革&#xff0c;面臨著供應鏈波動、個性化需求增長、可持續發展壓力以及技能人才短缺等多重挑戰。在這樣的背景下&#xff0c;技術創新不再是可有可無的選項&#xff0c;而是企業保持競爭力、實現可持…