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

溫馨提示×

溫馨提示×

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

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

SpringCloud負載均衡組件Ribbon源碼分析

發布時間:2022-07-18 14:09:41 來源:億速云 閱讀:175 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“SpringCloud負載均衡組件Ribbon源碼分析”,內容詳細,步驟清晰,細節處理妥當,希望這篇“SpringCloud負載均衡組件Ribbon源碼分析”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

    項目實戰

    創建項目

    同樣的,我們的項目現在依然有一個registry注冊中心,一個provider服務提供者,接下來,我們再次修改一下consumer服務消費者的代碼:

    @EnableEurekaClient
    @SpringBootApplication
    @RestController
    public class ConsumerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }
        @Bean
        @LoadBalanced
        RestTemplate restTemplate() {
            return new RestTemplate();
        }
        @Autowired
        DiscoveryClient discoveryClient;
        @Autowired
        RestTemplate restTemplate;
        @GetMapping("/hello2")
        public String hello2(String name) {
            String returnInfo = restTemplate.getForObject(  "http://provider/hello?name={1}", String.class, name);
            return returnInfo;
        }
    }

    在這個版本,同樣的,還是創建RestTemplate Bean對象,不同的是上面僅僅增加了@LoadBalanced注解。

    啟動項目驗證

    SpringCloud負載均衡組件Ribbon源碼分析

    依然正確返回了結果!

    太神奇了吧,比我們自己開發的負載均衡組件簡單太多了吧,僅僅在restTemplate() 方法上面增加了一個@LoadBalanced注解,怎么就實現的呢?廢話不說,為了一探究竟,扒一扒源碼吧!

    源碼分析

    首先,點擊@LoadBalanced注解進去,沒有什么特別之處,那么我們在想想,Spring在創建Bean實例的時候,注解在什么地方起了作用?什么?不知道?翻一下這篇文章吧:

    肝了兩周,一張圖解鎖Spring核心源碼

    通過回顧Spring啟動以及Bean的生命周期創建過程,我們就會發現加上@LoadBalancer注解后,項目啟動時就會加載LoadBalancerAutoConfiguration這個配置類(通過spring-cloud-commons包下面的的spring.factories)。通過查看該配置類源碼,發現其有個靜態內部類LoadBalancerInterceptorConfig,其內部又創建了一個負載均衡攔截器:LoadBalancerInterceptor,該攔截器包含有一個loadBalancerClient參數:

    @ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
        static class LoadBalancerInterceptorConfig {
            LoadBalancerInterceptorConfig() {
            }
            @Bean
            public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
                return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
            }
            @Bean
            @ConditionalOnMissingBean
            public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
                return (restTemplate) -> {
                    List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                    list.add(loadBalancerInterceptor);
                    restTemplate.setInterceptors(list);
                };
            }
        }

    我們繼續點擊LoadBalancerInterceptor類進入,發現intercept方法,該方法中調用了LoadBalancerClient的execute方法,

        public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
            URI originalUri = request.getURI();
            String serviceName = originalUri.getHost();
            Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
            return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
        }

    LoadBalancerClient是一個接口,點擊進去我們發現其實現類是RibbonLoadBalancerClient,查看其繼承關系:

    SpringCloud負載均衡組件Ribbon源碼分析

    通過接口中的方法名稱,我們可以猜想,choose方法就是選擇其中服務列表中其中一個服務,reconstructURI方法就是重新構造請求的URI。

    選擇服務

    choose方法是在RibbonLoadBalancerClient實現類中實現的

        public ServiceInstance choose(String serviceId, Object hint) {
            Server server = this.getServer(this.getLoadBalancer(serviceId), hint);
            return server == null ? null : new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
        }

    在這個方法中,先調用 getServer 方法獲取服務,這個方法最終會調用 ILoadBalancer 接口的 chooseServer 方法,而 ILoadBalancer 接口的實現類默認是ZoneAwareLoadBalancer。

    ZoneAwareLoadBalancer 繼承自 DynamicServerListLoadBalancer ,而在 DynamicServerListLoadBalancer 的構造方法中,調用了 this.restOfInit(clientConfig);在restOfInit這個方法中,通過 this.updateListOfServers()來獲取服務列表;

    而在chooseServer ()方法中,就會根據負載均衡算法,選擇其中一個服務并返回:

     BaseLoadBalancer zoneLoadBalancer = this.getLoadBalancer(zone);
     server = zoneLoadBalancer.chooseServer(key);

    地址替換

    選擇其中一個服務信息后,怎么將接口從 http://provider/hello 變為 http://localhost:8003/hello 呢?還記得上面我們說的reconstructURI方法嗎?通過配置類LoadBalancerAutoConfiguration加載后,會注入LoadBalancerInterceptor攔截器,該攔截器會攔截我們的請求,并對請求地址進行處理,重構方法的具體實現在 LoadBalancerContext 類的 reconstructURIWithServer 方法中

    public URI reconstructURIWithServer(Server server, URI original) {
            String host = server.getHost();
            int port = server.getPort();
            String scheme = server.getScheme();
            if (host.equals(original.getHost()) && port == original.getPort() && scheme == original.getScheme()) {
                return original;
            } else {
                if (scheme == null) {
                    scheme = original.getScheme();
                }
                if (scheme == null) {
                    scheme = (String)this.deriveSchemeAndPortFromPartialUri(original).first();
                }
                try {
                    StringBuilder sb = new StringBuilder();
                    sb.append(scheme).append("://");
                    if (!Strings.isNullOrEmpty(original.getRawUserInfo())) {
                        sb.append(original.getRawUserInfo()).append("@");
                    }
                    sb.append(host);
                    if (port >= 0) {
                        sb.append(":").append(port);
                    }
                    sb.append(original.getRawPath());
                    if (!Strings.isNullOrEmpty(original.getRawQuery())) {
                        sb.append("?").append(original.getRawQuery());
                    }
                    if (!Strings.isNullOrEmpty(original.getRawFragment())) {
                        sb.append("#").append(original.getRawFragment());
                    }
                    URI newURI = new URI(sb.toString());
                    return newURI;
                } catch (URISyntaxException var8) {
                    throw new RuntimeException(var8);
                }
            }
        }

    可以看到該方法中,將原始的請求地址original,替換成了選取的服務的IP和端口。并最終調用該服務的接口方法。

    讀到這里,這篇“SpringCloud負載均衡組件Ribbon源碼分析”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

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

    AI

    昌黎县| 来宾市| 西乡县| 鄂州市| 崇信县| 兴隆县| 萝北县| 铁力市| 延安市| 苍溪县| 金山区| 遵义县| 清丰县| 靖边县| 阿尔山市| 盐津县| 奉贤区| 嵩明县| 隆化县| 清远市| 阿克苏市| 永和县| 津市市| 万盛区| 西宁市| 大田县| 枞阳县| 吉安县| 甘南县| 芮城县| 凤山市| 巴东县| 开封市| 和田市| 浏阳市| 沽源县| 峨眉山市| 繁昌县| 安宁市| 和林格尔县| 临安市|