最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringCloud自定義loadbalancer實(shí)現(xiàn)標(biāo)簽路由的詳細(xì)方案

 更新時(shí)間:2025年02月15日 08:50:13   作者:煙味i  
本文介紹了通過標(biāo)簽路由解決前端開發(fā)環(huán)境接口調(diào)用慢的問題,實(shí)現(xiàn)方案包括在本地服務(wù)注冊(cè)元數(shù)據(jù)、自定義負(fù)載均衡器、以及網(wǎng)關(guān)配置等步驟,通過環(huán)境變量設(shè)置標(biāo)簽,網(wǎng)關(guān)根據(jù)請(qǐng)求頭中的標(biāo)簽進(jìn)行路由,從而實(shí)現(xiàn)前后端互不干擾的開發(fā)調(diào)試,感興趣的朋友一起看看吧

一、背景

  最近前端反應(yīng)開發(fā)環(huán)境有時(shí)候調(diào)接口會(huì)很慢,原因是有開發(fā)圖方便將本地服務(wù)注冊(cè)到開發(fā)環(huán)境,請(qǐng)求路由到開發(fā)本地導(dǎo)致,

為了解決該問題想到可以通過標(biāo)簽路由的方式避免該問題,實(shí)現(xiàn)前端聯(lián)調(diào)和開發(fā)自測互不干擾。

  該方案除了用于本地調(diào)試,還可以用于用戶灰度發(fā)布。

二、實(shí)現(xiàn)方案

  關(guān)于負(fù)載均衡,低版本的SpringCloud用的是Spring Cloud Ribbon,高版本用Spring Cloud LoadBalancer替代了,

Ribbon可以通過實(shí)現(xiàn)IRlue接口實(shí)現(xiàn),這里只介紹高版本的實(shí)現(xiàn)方案。

實(shí)現(xiàn)方案:

  • idea在環(huán)境變量中設(shè)置tag,本地服務(wù)啟動(dòng)時(shí)讀取環(huán)境變量將tag注冊(cè)到nacos的元數(shù)據(jù)

  • 重寫網(wǎng)關(guān)的負(fù)載均衡算法,從請(qǐng)求頭中獲取到的request-tag和服務(wù)實(shí)例的元數(shù)據(jù)進(jìn)行匹配,如果匹配到則返回對(duì)應(yīng)的

    服務(wù)實(shí)例,否則提示服務(wù)未找到。

三、編碼實(shí)現(xiàn)

3.1 order服務(wù)

新建一個(gè)SpringCloud服務(wù)order-service,注冊(cè)元數(shù)據(jù)很簡單,只需要排除掉NacosDiscoveryClientConfiguration,再寫一個(gè)自己的NacosDiscoveryClientConfiguration配置類即可。

創(chuàng)建MyNacosDiscoveryClientConfiguration

/**
 * @Author: Ship
 * @Description:
 * @Date: Created in 2025/2/12
 */
@Configuration(
        proxyBeanMethods = false
)
@ConditionalOnDiscoveryEnabled
@ConditionalOnBlockingDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
@AutoConfigureBefore({SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class})
@AutoConfigureAfter({NacosDiscoveryAutoConfiguration.class})
public class MyNacosDiscoveryClientConfiguration {
    @Bean
    public DiscoveryClient nacosDiscoveryClient(NacosServiceDiscovery nacosServiceDiscovery) {
        return new NacosDiscoveryClient(nacosServiceDiscovery);
    }
    @Bean
    @ConditionalOnProperty(
            value = {"spring.cloud.nacos.discovery.watch.enabled"},
            matchIfMissing = true
    )
    public NacosWatch nacosWatch(NacosServiceManager nacosServiceManager, NacosDiscoveryProperties nacosDiscoveryProperties,
                                 ObjectProvider<ThreadPoolTaskScheduler> taskExecutorObjectProvider, Environment environment) {
        // 環(huán)境變量讀取標(biāo)簽
        String tag = environment.getProperty("tag");
        nacosDiscoveryProperties.getMetadata().put("request-tag", tag);
        return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties, taskExecutorObjectProvider);
    }
}

這里代碼基本與NacosDiscoveryClientConfiguration一致,只是加上了設(shè)置元數(shù)據(jù)的邏輯。

@SpringBootApplication(exclude = NacosDiscoveryClientConfiguration.class)
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

啟動(dòng)類上需要排除默認(rèn)的NacosDiscoveryClientConfiguration,不然啟動(dòng)會(huì)報(bào)bean重復(fù)注冊(cè)的錯(cuò)誤,或者配置添加spring.main.allow-bean-definition-overriding=true允許重復(fù)注冊(cè)也行。

寫一個(gè)測試接口,方便后面測試

/**
 * @Author: Ship
 * @Description:
 * @Date: Created in 2025/2/12
 */
@RequestMapping("test")
@RestController
public class TestController {
    @GetMapping("")
    public String sayHello(){
        return "hello";
    }
}

3.2 gateway服務(wù)

新建一個(gè)網(wǎng)關(guān)服務(wù),pom文件如下:

 <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2020.0.3</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
        <spring-boot.version>2.5.1</spring-boot.version>
        <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${spring-boot.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-bootstrap</artifactId>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

Spring-Cloud-loadBalancer默認(rèn)使用輪詢的算法,即org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer類實(shí)現(xiàn),因此可以參考RoundRobinLoadBalancer實(shí)現(xiàn)一個(gè)TagLoadBalancer,代碼如下:

/**
 * @Author: Ship
 * @Description:
 * @Date: Created in 2025/2/12
 */
public class TagLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private static final String TAG_HEADER = "request-tag";
    private static final Log log = LogFactory.getLog(TagLoadBalancer.class);
    final AtomicInteger position;
    final String serviceId;
    ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    public TagLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this(serviceInstanceListSupplierProvider, serviceId, (new Random()).nextInt(1000));
    }
    public TagLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.position = new AtomicInteger(seedPosition);
    }
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier) this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next().map((serviceInstances) -> {
            return this.processInstanceResponse(supplier, serviceInstances, request);
        });
    }
    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances, Request request) {
        Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances, request);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance((ServiceInstance) serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }
    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {
        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("No servers available for service: " + this.serviceId);
            }
            return new EmptyResponse();
        }
        if (request instanceof DefaultRequest) {
            DefaultRequest<RequestDataContext> defaultRequest = (DefaultRequest) request;
            // 上下文獲取請(qǐng)求頭
            HttpHeaders headers = defaultRequest.getContext().getClientRequest().getHeaders();
            List<String> list = headers.get(TAG_HEADER);
            if (!CollectionUtils.isEmpty(list)) {
                String requestTag = list.get(0);
                for (ServiceInstance instance : instances) {
                    String str = instance.getMetadata().getOrDefault(TAG_HEADER, "");
                    if (requestTag.equals(str)) {
                        return new DefaultResponse(instance);
                    }
                }
                log.error(String.format("No servers available for service:%s,tag:%s ", this.serviceId, requestTag));
                return new EmptyResponse();
            }
        }
        int pos = Math.abs(this.position.incrementAndGet());
        ServiceInstance instance = instances.get(pos % instances.size());
        return new DefaultResponse(instance);
    }
}

這里需要實(shí)現(xiàn)ReactorServiceInstanceLoadBalancer接口,如果請(qǐng)求頭帶有標(biāo)簽則根據(jù)標(biāo)簽路由,否則使用默認(rèn)的輪詢算法。

還要把TagLoadBalancer用起來,所以需要定義一個(gè)配置類TagLoadBalancerConfig,并通過@LoadBalancerClients注解添加默認(rèn)配置,代碼如下:

/**
 * @Author: Ship
 * @Description:
 * @Date: Created in 2025/2/12
 */
public class TagLoadBalancerConfig {
    @Bean
    public ReactorLoadBalancer reactorTagLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new TagLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}
@LoadBalancerClients(defaultConfiguration = {TagLoadBalancerConfig.class})
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

最后在application.yml文件添加網(wǎng)關(guān)路由配置

spring:
  application:
    name: gateway
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        namespace: dev
        group: DEFAULT_GROUP
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: dev
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            - StripPrefix=1
server:
  port: 9000

3.3 代碼測試

本地啟動(dòng)nacos后啟動(dòng)order(注意需要在idea設(shè)置環(huán)境變量tag=ship)和gateway服務(wù),可以看到order服務(wù)已經(jīng)成功注冊(cè)了元數(shù)據(jù)

  • 然后用Postman請(qǐng)求網(wǎng)關(guān)http://localhost:9000/order/test

可以看到請(qǐng)求成功路由到了order服務(wù),說明根據(jù)tag路由成功了。

去掉環(huán)境變量tag后重新啟動(dòng)Order服務(wù),再次請(qǐng)求響應(yīng)報(bào)文如下:

{
    "timestamp": "2025-02-14T12:10:44.294+00:00",
    "path": "/order/test",
    "status": 503,
    "error": "Service Unavailable",
    "requestId": "41651188-4"
}

說明根據(jù)requst-tag找不到對(duì)應(yīng)的服務(wù)實(shí)例,代碼邏輯生效了。

四、總結(jié)

  聰明的人已經(jīng)發(fā)現(xiàn)了,本文只實(shí)現(xiàn)了網(wǎng)關(guān)路由到下游服務(wù)這部分的標(biāo)簽路由,下游服務(wù)A調(diào)服務(wù)B的標(biāo)簽路由并未實(shí)現(xiàn),其實(shí)現(xiàn)方案也不難,只需要通過上下文傳遞+feign攔截器就可以做到全鏈路的標(biāo)簽路由,有興趣的可以自己試試。

  本文代碼已上傳github,順便推廣下前段時(shí)間寫的idea插件CodeFaster(快速生成常用流操作的代碼,Marketplace搜索下載即可體驗(yàn))??。

到此這篇關(guān)于SpringCloud自定義loadbalancer實(shí)現(xiàn)標(biāo)簽路由的詳細(xì)方案的文章就介紹到這了,更多相關(guān)SpringCloud標(biāo)簽路由內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談springboot 屬性定義

    淺談springboot 屬性定義

    本篇文章主要介紹了淺談springboot 屬性定義,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • MyBatis-Plus聯(lián)表查詢(Mybatis-Plus-Join)的功能實(shí)現(xiàn)

    MyBatis-Plus聯(lián)表查詢(Mybatis-Plus-Join)的功能實(shí)現(xiàn)

    mybatis-plus作為mybatis的增強(qiáng)工具,簡化了開發(fā)中的數(shù)據(jù)庫操作,這篇文章主要介紹了MyBatis-Plus聯(lián)表查詢(Mybatis-Plus-Join),需要的朋友可以參考下
    2022-08-08
  • 不知道面試會(huì)不會(huì)問Lambda怎么用(推薦)

    不知道面試會(huì)不會(huì)問Lambda怎么用(推薦)

    這篇文章主要介紹了Lambda表達(dá)式用法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Java實(shí)現(xiàn)視頻自定義裁剪功能

    Java實(shí)現(xiàn)視頻自定義裁剪功能

    這篇文章主要介紹了如何通過java實(shí)現(xiàn)視頻裁剪,可以將視頻按照自定義尺寸進(jìn)行裁剪,文中的示例代碼簡潔易懂,感興趣的可以了解一下
    2022-01-01
  • 繼承WebMvcConfigurationSupport后自動(dòng)配置不生效及如何配置攔截器

    繼承WebMvcConfigurationSupport后自動(dòng)配置不生效及如何配置攔截器

    這篇文章主要介紹了繼承WebMvcConfigurationSupport后自動(dòng)配置不生效及如何配置攔截器,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • SpringBoot如何使用自定義注解實(shí)現(xiàn)接口限流

    SpringBoot如何使用自定義注解實(shí)現(xiàn)接口限流

    這篇文章主要介紹了SpringBoot如何使用自定義注解實(shí)現(xiàn)接口限流,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 談?wù)剬?duì)Java多態(tài)性的一點(diǎn)理解

    談?wù)剬?duì)Java多態(tài)性的一點(diǎn)理解

    多態(tài)就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量倒底會(huì)指向哪個(gè)類的實(shí)例對(duì)象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類中實(shí)現(xiàn)的方法,必須在由程序運(yùn)行期間才能決定
    2017-08-08
  • Java基礎(chǔ)知識(shí)之BufferedReader流的使用

    Java基礎(chǔ)知識(shí)之BufferedReader流的使用

    這篇文章主要介紹了Java基礎(chǔ)知識(shí)之BufferedReader流的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 淺談關(guān)于Java正則和轉(zhuǎn)義中\(zhòng)\和\\\\的理解

    淺談關(guān)于Java正則和轉(zhuǎn)義中\(zhòng)\和\\\\的理解

    這篇文章主要介紹了淺談關(guān)于Java正則和轉(zhuǎn)義中\(zhòng)\和\\\\的理解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)

    jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)

    本篇文章主要介紹了jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)。具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-04-04

最新評(píng)論

东山县| 永丰县| 云南省| 平邑县| 高州市| 新绛县| 额尔古纳市| 乐山市| 即墨市| 合作市| 军事| 新兴县| 华亭县| 静宁县| 山西省| 合川市| 张北县| 远安县| 依兰县| 万宁市| 吉安县| 响水县| 明光市| 观塘区| 调兵山市| 金昌市| 洛隆县| 天台县| 贡觉县| 景泰县| 武强县| 新源县| 五常市| 墨江| 鄂尔多斯市| 苍山县| 云和县| 双江| 于田县| 贵南县| 扎兰屯市|