中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么使用SpringCloud負載均衡實現定向路由

發布時間:2022-08-25 17:58:35 來源:億速云 閱讀:219 作者:iii 欄目:開發技術

這篇文章主要講解了“怎么使用SpringCloud負載均衡實現定向路由”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么使用SpringCloud負載均衡實現定向路由”吧!

    背景

    隨著微服務項目的迭代,可能一個服務會有多個實例,這時候就會涉及到負載均衡。然而我們在開發的時候,肯定希望只啟動一個項目。然后調試的時候希望負載均衡把請求分配到我們正在開發測試的服務實例上面。

    實現方式

    基于ip

    這個很好理解,就是開發者本地正在運行的服務在nacos上面肯定顯示你本機的ip;那么只要我得到開發者的ip就能夠根據這個ip來過濾nacos上面的服務,達到定向路由的效果。

    基于nacos的元數據

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

    在yaml中配置nacos元數據的version屬性。前端在請求header中添加與其對應version屬性,就可以實現服務過濾也就是定向路由。

    實現原理

    Gateway服務

    因為gateway底層的不同,所以其負載均衡也與普通服務的不同,因此要特殊處理。先看gateway中load balancer組件調用流程。

    首先在gateway中load balancer本身也是一個過濾器,所以流程如下。

    • ReactiveLoadBalancerClientFilter里面有個LoadBalancerClientFactory屬性,通過這個工廠獲取具體的負載均衡器

    • LoadBalancerClientFactory會載入LoadBalancerClientConfiguration配置

    • LoadBalancerClientConfiguration會初始化我們需要的RoundRobinLoadBalancer,并且會通過構造函數傳入LoadBalancerClientFactory對象。

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

    • 實現自己的LoadBalancerClientFactory,傳入自己LoadBalancerClientConfiguration

    • 在自己的LoadBalancerClientConfiguration初始化自己的RoundRobinLoadBalancer

    • 最后在自己的ReactiveLoadBalancerClientFilter里面傳入自己的LoadBalancerClientFactory,獲得自己的負載均衡器。

    具體源碼(只放核心)

    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("自定義負載均衡器,超時等待異常");
        } finally {
            lock.unlock();
        }
    }
    // 根據附加信息過濾服務
    private List<ServiceInstance> filterServiceInstance(ServerWebExchange exchange, List<ServiceInstance> serviceInstances) {
    
        List<ServiceInstance> filteredServices = new ArrayList<>();
        // 自動模式
        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("未發現符合ip或metadata.version服務,將采用原始服務集合");
            return serviceInstances;
        }
        return filteredServices;
    }
    // 自動模式
    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;
    }
    //元數據模式
    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失敗,無法進行定向路由");
        }
        return filteredServices;
    }

    MyLoadBalancerClientFactory

    public class MyLoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification>
          implements ReactiveLoadBalancer.Factory<ServiceInstance>{
    
        ................
    
        public MyLoadBalancerClientFactory() {
            // 傳入自己的自動配置
            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);
            // 初始化自己的負載均衡器
            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里面擴展choose方法,使其接受ServerWebExchange參數
            // 傳入ServerWebExchange我們就可以,獲取請求信息,方便我們過濾
            return loadBalancer.choose(exchange);
        }
    }

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

    普通服務

    普通服務實現自定義負載均衡器就很簡單了,實現自定義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:
            // 重點
              version: mfine
    celi-dev:
      config:
        model: "metadata"

    nacos中服務元數據

    然后請求頭中附帶version信息

    自定義負載均衡器會通過請求頭中的version去nacos中注冊服務的元數據里面去比對version信息。

    ip模式

    配置yaml

    celi-dev:
      config:
        model: "ip"
    • 在header中指定IP

    • 依靠請求信息獲取ip

    配置yaml就好

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

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

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

    auto模式

    配置yaml,其實可以不配置。

    celi-dev:
      config:
        model: "auto"

    自動模式默認先使用ip模式獲取不到ip會自動切換metadata模式。

    什么都不配置,默認auto模式

    感謝各位的閱讀,以上就是“怎么使用SpringCloud負載均衡實現定向路由”的內容了,經過本文的學習后,相信大家對怎么使用SpringCloud負載均衡實現定向路由這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    新巴尔虎右旗| 双江| 眉山市| 宣武区| 望都县| 唐海县| 贵阳市| 五莲县| 康乐县| 泊头市| 马山县| 大田县| 濮阳市| 商城县| 清水县| 辽阳市| 勃利县| 马鞍山市| 云南省| 叶城县| 津南区| 普兰店市| 郓城县| 方正县| 民和| 肃南| 江华| 顺义区| 竹溪县| 阳城县| 深州市| 高台县| 金乡县| 金溪县| 普定县| 鄂托克前旗| 玉树县| 马鞍山市| 年辖:市辖区| 南溪县| 嘉兴市|