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

SpringCloud負(fù)載均衡實(shí)現(xiàn)定向路由詳情

 更新時(shí)間:2022年08月19日 11:44:58   作者:LogicMF  
這篇文章主要介紹了SpringCloud負(fù)載均衡實(shí)現(xiàn)定向路由詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

背景

隨著微服務(wù)項(xiàng)目的迭代,可能一個(gè)服務(wù)會(huì)有多個(gè)實(shí)例,這時(shí)候就會(huì)涉及到負(fù)載均衡。然而我們在開發(fā)的時(shí)候,肯定希望只啟動(dòng)一個(gè)項(xiàng)目。然后調(diào)試的時(shí)候希望負(fù)載均衡把請求分配到我們正在開發(fā)測試的服務(wù)實(shí)例上面。

如圖所示,我們希望可以指定調(diào)用路徑也就是定向路由。

實(shí)現(xiàn)方式

基于ip

這個(gè)很好理解,就是開發(fā)者本地正在運(yùn)行的服務(wù)在nacos上面肯定顯示你本機(jī)的ip;那么只要我得到開發(fā)者的ip就能夠根據(jù)這個(gè)ip來過濾nacos上面的服務(wù),達(dá)到定向路由的效果。

基于nacos的元數(shù)據(jù)

spring:
  cloud:
    nacos:
      discovery:
        metadata: 
          version: "mfine"

在yaml中配置nacos元數(shù)據(jù)的version屬性。前端在請求header中添加與其對應(yīng)version屬性,就可以實(shí)現(xiàn)服務(wù)過濾也就是定向路由。

實(shí)現(xiàn)原理

Gateway服務(wù)

因?yàn)間ateway底層的不同,所以其負(fù)載均衡也與普通服務(wù)的不同,因此要特殊處理。先看gateway中l(wèi)oad balancer組件調(diào)用流程。

首先在gateway中l(wèi)oad balancer本身也是一個(gè)過濾器,所以流程如下。

  • ReactiveLoadBalancerClientFilter里面有個(gè)LoadBalancerClientFactory屬性,通過這個(gè)工廠獲取具體的負(fù)載均衡器
  • LoadBalancerClientFactory會(huì)載入LoadBalancerClientConfiguration配置
  • LoadBalancerClientConfiguration會(huì)初始化我們需要的RoundRobinLoadBalancer,并且會(huì)通過構(gòu)造函數(shù)傳入LoadBalancerClientFactory對象。

那我們要做什么呢?其實(shí)就是截胡。

  • 實(shí)現(xiàn)自己的LoadBalancerClientFactory,傳入自己LoadBalancerClientConfiguration
  • 在自己的LoadBalancerClientConfiguration初始化自己的RoundRobinLoadBalancer
  • 最后在自己的ReactiveLoadBalancerClientFilter里面?zhèn)魅胱约旱?strong>LoadBalancerClientFactory,獲得自己的負(fù)載均衡器。

具體源碼(只放核心)

MyRoundRobinLoadBalancer

private Response<ServiceInstance> getInstanceResponse(
    List<ServiceInstance> instances, ServerWebExchange exchange) {
    if (instances.isEmpty()) {
        log.warn("No servers available for service: " + this.serviceId);
        return new EmptyResponse();
    }
    try {
        //可重入鎖
        if (this.lock.tryLock(10, TimeUnit.SECONDS))
            instances = this.filterServiceInstance(exchange, instances);
        // TODO: enforce order?
        int pos = Math.abs(this.position.incrementAndGet());
        ServiceInstance instance = instances.get(pos % instances.size());
        return new DefaultResponse(instance);
    } catch (InterruptedException e) {
        throw new RuntimeException("自定義負(fù)載均衡器,超時(shí)等待異常");
    } finally {
        lock.unlock();
    }
}
// 根據(jù)附加信息過濾服務(wù)
private List<ServiceInstance> filterServiceInstance(ServerWebExchange exchange, List<ServiceInstance> serviceInstances) {

    List<ServiceInstance> filteredServices = new ArrayList<>();
    // 自動(dòng)模式
    if (DevConfigEnum.AUTO.getCode().equals(this.properties.getModel())) {
        filteredServices = autoModel(exchange, serviceInstances);
    }
    // ip 模式
    if (this.properties.getModel().equals(DevConfigEnum.IP.getCode())) {
        filteredServices = ipModel(exchange, serviceInstances);
    }
    // metadata 模式
    if (this.properties.getModel().equals(DevConfigEnum.METADATA.getCode())) {
        filteredServices = metadataModel(exchange, serviceInstances);
    }
    if (filteredServices.isEmpty()) {
        log.info("未發(fā)現(xiàn)符合ip或metadata.version服務(wù),將采用原始服務(wù)集合");
        return serviceInstances;
    }
    return filteredServices;
}
// 自動(dòng)模式
private List<ServiceInstance> autoModel(ServerWebExchange exchange, List<ServiceInstance> serviceInstances) {
    List<ServiceInstance> filteredServices;

    filteredServices = ipModel(exchange, serviceInstances);
    if (filteredServices.isEmpty()) {
        filteredServices = metadataModel(exchange, serviceInstances);
    }
    return filteredServices;
}
//元數(shù)據(jù)模式
private List<ServiceInstance> metadataModel(ServerWebExchange exchange, List<ServiceInstance> serviceInstances) {
    String version = exchange.getRequest().getHeaders().getFirst("version");
    List<ServiceInstance> filteredServices = new ArrayList<>();
    if (version != null) {
        log.info("version模式:獲取metadata.version成功");
        filteredServices = serviceInstances.stream().filter(instance -> {
            String metaVersion = instance.getMetadata().get("version");
            if (metaVersion == null) {
                return false;
            }
            return metaVersion.equals(version);
        }).collect(Collectors.toList());
    }
    return filteredServices;
}
// ip模式
private List<ServiceInstance> ipModel(ServerWebExchange exchange, List<ServiceInstance> serviceInstances) {
    List<ServiceInstance> filteredServices = new ArrayList<>();
    try {
        String ipAddress = exchange.getRequest().getHeaders().getFirst("ip");
        if (ipAddress == null) {
            ipAddress = IPUtils.getIpAddress(exchange.getRequest());
        }
        log.warn("ip模式:獲取ip成功");
        String finalIpAddress = ipAddress;
        filteredServices = serviceInstances.stream().filter(item -> item.getHost().equals(finalIpAddress))
            .collect(Collectors.toList());
    } catch (UnknownHostException e) {
        log.warn("ip模式:獲取ip失敗,無法進(jìn)行定向路由");
    }
    return filteredServices;
}

MyLoadBalancerClientFactory

public class MyLoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification>
      implements ReactiveLoadBalancer.Factory<ServiceInstance>{

    ................

    public MyLoadBalancerClientFactory() {
        // 傳入自己的自動(dòng)配置
        super(MyLoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);
    }

    ...........
}

MyLoadBalancerClientConfiguration

@Configuration
public class MyLoadBalancerClientConfiguration {
    @Autowired
    private MicroServiceDevConfigProperties microServiceDevConfigProperties;
    @Bean
    public ReactorServiceInstanceLoadBalancer reactiveLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        // 初始化自己的負(fù)載均衡器
        return new MyRoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name, 1000,microServiceDevConfigProperties);
    }
}
@Configuration
public class MyReactiveLoadBalancerClientFilter extends ReactiveLoadBalancerClientFilter {
    private static final Log log = LogFactory
            .getLog(ReactiveLoadBalancerClientFilter.class);
    private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
    private final MyLoadBalancerClientFactory clientFactory;
    private LoadBalancerProperties properties;
    // 注入自己的LoadBalancerClientFactory
    public MyReactiveLoadBalancerClientFilter(MyLoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
        super(null, null);
        this.clientFactory = clientFactory;
        this.properties = properties;
    }
    ........
    private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
        URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
        //獲得自己load balancer
        MyRoundRobinLoadBalancer loadBalancer = this.clientFactory
                .getInstance(uri.getHost(), MyRoundRobinLoadBalancer.class);
        if (loadBalancer == null) {
            throw new NotFoundException("No loadbalancer available for " + uri.getHost());
        }
        // 在自己的load balancer里面擴(kuò)展choose方法,使其接受ServerWebExchange參數(shù)
        // 傳入ServerWebExchange我們就可以,獲取請求信息,方便我們過濾
        return loadBalancer.choose(exchange);
    }
}

我的這種實(shí)現(xiàn)較為繁瑣,可能大家有更好方式,大家要是有更好更簡單的方法,也可以直接替換掉。

普通服務(wù)

普通服務(wù)實(shí)現(xiàn)自定義負(fù)載均衡器就很簡單了,實(shí)現(xiàn)自定義RoundRobinRule就可以了

@Configuration
public class MyRoundRobinLoadBalancer extends RoundRobinRule {
    //不同的使用注入的方式獲取請求信息
    @Autowired
    private HttpServletRequest request;
    .....
        private List<Server> filterServers(List<Server> reachableServers) {
        List<Server> servers = new ArrayList<>();
        if (this.properties.getModel().equals(DevConfigEnum.AUTO.getCode())) {
            servers = ipModel(reachableServers);
            if (servers.isEmpty()) {
                servers = metadataModel(reachableServers);
            }
        }
        if (this.properties.getModel().equals(DevConfigEnum.IP.getCode())) {
            servers = ipModel(reachableServers);
        }
        if (this.properties.getModel().equals(DevConfigEnum.METADATA.getCode())) {
            servers = metadataModel(reachableServers);
        }
        if (servers.isEmpty()) {
            return reachableServers;
        }
        return servers;
    }

    private List<Server> metadataModel(List<Server> reachableServers) {
        String version = request.getHeader("version");
        List<Server> servers = new ArrayList<>();
        if (version != null) {
            log.info("metadata模式: 獲取version成功");
            servers = reachableServers.stream().filter(item -> {
                NacosServer nacosServer = (NacosServer) item;
                String metaVersion = nacosServer.getMetadata().get("version");
                if (metaVersion == null) {
                    return false;
                }
                return metaVersion.equals(version);
            }).collect(Collectors.toList());
        } else {
            log.warn("metadata模式: header中無version字段且未獲取到請求者ip");
        }
        return servers;
    }

    private List<Server> ipModel(List<Server> reachableServers) {
        List<Server> servers = new ArrayList<>();
        try {
            String ip = this.request.getHeader("ip");
            if (ip == null) {
                ip = IPUtils.getIpAddress(request);
            }
            String finalIp = ip;
            servers = reachableServers.stream().filter(item -> item.getHost().equals(finalIp)).collect(Collectors.toList());
            log.info("ip模式: 獲取請求者ip成功");
        } catch (UnknownHostException e) {
            log.warn("ip模式: 獲取ip失敗");
        }
        return servers;
    }
    ........
}

深入思考一下,通過注入的方式獲取request信息是否存在多線程安全問題呢?

使用方法

metadata模式

配置yaml:

spring:
  application:
    name: cloud-order
  cloud:
    nacos:
      discovery:
        metadata:
        // 重點(diǎn)
          version: mfine
celi-dev:
  config:
    model: "metadata"

nacos中服務(wù)元數(shù)據(jù)

然后請求頭中附帶version信息

自定義負(fù)載均衡器會(huì)通過請求頭中的version去nacos中注冊服務(wù)的元數(shù)據(jù)里面去比對version信息。

ip模式

配置yaml

celi-dev:
  config:
    model: "ip"
  • 在header中指定IP
  • 依靠請求信息獲取ip

配置yaml就好

此不指定ip的時(shí)候,后臺(tái)獲取的ip可能不對。取決你本地是否存在多張網(wǎng)卡(虛擬網(wǎng)卡也算),有時(shí)候nacos中ip顯示也會(huì)是虛擬網(wǎng)卡的ip。

使用前請確認(rèn)你的服務(wù)在nacos中的ip是多少,然后在header中指定ip,這樣最省事也最穩(wěn)妥。

一般是先從header中獲取ip信息,獲取不到再從request對象中分析。

auto模式

配置yaml,其實(shí)可以不配置。

celi-dev:
  config:
    model: "auto"

自動(dòng)模式默認(rèn)先使用ip模式獲取不到ip會(huì)自動(dòng)切換metadata模式。

什么都不配置,默認(rèn)auto模式

總結(jié)

三種模式里面meta模式最繁瑣,心智負(fù)擔(dān)最重,但是也是最簡單的。ip模式難度在于獲取ip的準(zhǔn)確性因此加入指定ip的方式。自動(dòng)模式則二者結(jié)合。

到此這篇關(guān)于SpringCloud負(fù)載均衡實(shí)現(xiàn)定向路由詳情的文章就介紹到這了,更多相關(guān)SpringCloud負(fù)載均衡內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot創(chuàng)建線程池的兩種方式小結(jié)

    springboot創(chuàng)建線程池的兩種方式小結(jié)

    這篇文章主要介紹了springboot創(chuàng)建線程池的兩種方式小結(jié),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • SpringCloud Gateway中斷言路由和過濾器的使用詳解

    SpringCloud Gateway中斷言路由和過濾器的使用詳解

    這篇文章主要介紹了SpringCloud Gateway中斷言路由和過濾器的使用,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 接口隔離原則_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    接口隔離原則_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了接口隔離原則,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • spring cloud 阿波羅 apollo 本地開發(fā)環(huán)境搭建過程

    spring cloud 阿波羅 apollo 本地開發(fā)環(huán)境搭建過程

    Apollo(阿波羅)是攜程框架部門研發(fā)的配置管理平臺(tái),能夠集中化管理應(yīng)用不同環(huán)境、不同集群的配置,配置修改后能夠?qū)崟r(shí)推送到應(yīng)用端,并且具備規(guī)范的權(quán)限、流程治理等特性
    2018-01-01
  • Java的后臺(tái)文件夾下文件的遍歷完整代碼

    Java的后臺(tái)文件夾下文件的遍歷完整代碼

    這篇文章主要介紹了Java的后臺(tái)文件夾下文件的遍歷完整代碼,首先分享了java中遍歷一個(gè)文件夾里邊的所有文件,然后介紹了用Java遍歷一個(gè)文件夾并獲取它里面的所有內(nèi)容詳細(xì)代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • Java設(shè)計(jì)模式七大原則之開閉原則詳解

    Java設(shè)計(jì)模式七大原則之開閉原則詳解

    開閉原則,又稱為OCP原則,即一個(gè)軟件實(shí)體如類,模塊和函數(shù)應(yīng)該對擴(kuò)展開放,對修改關(guān)閉。本文將詳細(xì)介紹Java設(shè)計(jì)模式七大原則之一的開閉原則,需要的可以參考一下
    2022-02-02
  • Java基礎(chǔ)教程之八大基本數(shù)據(jù)類型

    Java基礎(chǔ)教程之八大基本數(shù)據(jù)類型

    這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)教程之八大基本數(shù)據(jù)類型的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Java類成員訪問權(quán)限控制知識(shí)總結(jié)

    Java類成員訪問權(quán)限控制知識(shí)總結(jié)

    這篇文章主要介紹了Java類成員訪問權(quán)限控制知識(shí)總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • elasticsearch kibana簡單查詢講解

    elasticsearch kibana簡單查詢講解

    今天小編就為大家分享一篇關(guān)于elasticsearch kibana簡單查詢講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • Java正則表達(dá)式,提取雙引號(hào)中間的部分方法

    Java正則表達(dá)式,提取雙引號(hào)中間的部分方法

    今天小編就為大家分享一篇Java正則表達(dá)式,提取雙引號(hào)中間的部分方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07

最新評論

滨州市| 福州市| 鄂托克前旗| 郑州市| 扎赉特旗| 大理市| 江都市| 绵竹市| 含山县| 钟山县| 锡林郭勒盟| 来宾市| 达孜县| 马关县| 翁牛特旗| 大冶市| 陆河县| 噶尔县| 邯郸市| 恭城| 勃利县| 仙居县| 奉化市| 芮城县| 湾仔区| 姚安县| 阳谷县| 咸宁市| 新田县| 保靖县| 萍乡市| 宁武县| 阳江市| 盖州市| 巴青县| 衢州市| 贡嘎县| 修水县| 永善县| 渝中区| 九江县|