1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        SpringCloud系列之客戶端負(fù)載均衡Netflix Ribbon

        共 10194字,需瀏覽 21分鐘

         ·

        2020-12-10 02:44

        走過路過不要錯過

        點(diǎn)擊藍(lán)字關(guān)注我們


        1. 什么是負(fù)載均衡?

        負(fù)載均衡是一種基礎(chǔ)的網(wǎng)絡(luò)服務(wù),它的核心原理是按照指定的負(fù)載均衡算法,將請求分配到后端服務(wù)集群上,從而為系統(tǒng)提供并行處理和高可用的能力。提到負(fù)載均衡,你可能想到nginx。對于負(fù)載均衡,一般分為服務(wù)端負(fù)載均衡和客戶端負(fù)載均衡

        • 服務(wù)端負(fù)載均衡:在消費(fèi)者和服務(wù)提供方中間使用獨(dú)立的代理方式進(jìn)行負(fù)載,有硬件的負(fù)載均衡器,比如 F5,也有軟件,比如 Nginx。

        • 客戶端負(fù)載均衡:所謂客戶端負(fù)載均衡,就是客戶端根據(jù)自己的請求情況做負(fù)載,本文介紹的Netflix Ribbon就是客戶端負(fù)載均衡的組件

        2. 什么是Netflix Ribbon?

        在上一章的學(xué)習(xí)中,我們知道了微服務(wù)的基本概念,知道怎么基于Ribbon+restTemplate的方式實(shí)現(xiàn)服務(wù)調(diào)用,接著上篇博客,我們再比較詳細(xì)學(xué)習(xí)客戶端負(fù)載均衡Netflix Ribbon,學(xué)習(xí)本博客之前請先學(xué)習(xí)上篇博客,然后再學(xué)習(xí)本篇博客

        Ribbon 是由 Netflix 發(fā)布的負(fù)載均衡器,它有助于控制 HTTP 和 TCP 的客戶端的行為。Ribbon 屬于客戶端負(fù)載均衡。

        3. Netflix Ribbon實(shí)驗(yàn)環(huán)境準(zhǔn)備

        環(huán)境準(zhǔn)備:

        • JDK 1.8

        • SpringBoot2.2.3

        • SpringCloud(Hoxton.SR6)

        • Maven 3.2+

        • 開發(fā)工具

          • IntelliJ IDEA

          • smartGit

        創(chuàng)建一個SpringBoot Initialize項(xiàng)目,詳情可以參考我之前博客:SpringBoot系列之快速創(chuàng)建項(xiàng)目教程

        可以引入Eureka Discovery Client,也可以單獨(dú)添加Ribbon

        Spring Cloud Hoxton.SR6版本不需要引入spring-cloud-starter-netflix-ribbon,已經(jīng)默認(rèn)集成

        也可以單獨(dú)添加Ribbon依賴:

        本博客的是基于spring-cloud-starter-netflix-eureka-client進(jìn)行試驗(yàn),試驗(yàn)前要運(yùn)行eureka服務(wù)端,eureka服務(wù)提供者,代碼請參考上一章博客

        補(bǔ)充:IDEA中多實(shí)例運(yùn)行方法

        step1:如圖,不要加上勾選

        step2:指定不同的server端口和實(shí)例id,如圖:

        啟動成功后,是可以看到多個實(shí)例的

        4. Netflix Ribbon API使用

        使用LoadBalancerClient

         @Autowired
        LoadBalancerClient loadBalancerClient;

        @Test
        void contextLoads() {
        ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKA-SERVICE-PROVIDER");
        URI uri = URI.create(String.format("http://%s:%s", serviceInstance.getHost() , serviceInstance.getPort()));
        System.out.println(uri.toString());
        }

        構(gòu)建BaseLoadBalancer實(shí)例例子:

         @Test
        void testLoadBalancer(){
        // 服務(wù)列表
        List serverList = Arrays.asList(new Server("localhost", 8083), new Server("localhost", 8084));
        // 構(gòu)建負(fù)載實(shí)例
        BaseLoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
        loadBalancer.setRule(new RandomRule());
        for (int i = 0; i < 5; i++) {
        String result = LoadBalancerCommand.builder().withLoadBalancer(loadBalancer).build()
        .submit(new ServerOperation() {
        public Observable call(Server server) {
        try {
        String address = "http://" + server.getHost() + ":" + server.getPort()+"/EUREKA-SERVICE-PROVIDER/api/users/mojombo";
        System.out.println("調(diào)用地址:" + address);
        return Observable.just("");
        } catch (Exception e) {
        return Observable.error(e);
        }
        }
        }).toBlocking().first();
        System.out.println("result:" + result);
        }
        }

        5. 負(fù)載均衡@LoadBalanced

        Ribbon負(fù)載均衡實(shí)現(xiàn),RestTemplate 要加上@LoadBalanced

        package com.example.springcloud.ribbon.configuration;

        import org.springframework.cloud.client.loadbalancer.LoadBalanced;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;
        import org.springframework.web.client.RestTemplate;

        /**
        *

        * RestConfiguration
        *

        *
        *

        * @author mazq
        * 修改記錄
        * 修改后版本: 修改人:修改日期: 2020/07/31 09:43 修改內(nèi)容:
        *

        */

        @Configuration
        public class RestConfiguration {
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate() {
        return new RestTemplate();
        }
        }

        yaml配置:

        server:
        port: 8082
        spring:
        application:
        name: eureka-service-consumer
        eureka:
        client:
        service-url:
        defaultZone: http://localhost:8761/eureka/
        fetch-registry: true
        register-with-eureka: false
        healthcheck:
        enabled: false
        instance:
        status-page-url-path: http://localhost:8761/actuator/info
        health-check-url-path: http://localhost:8761/actuator//health
        prefer-ip-address: true
        instance-id: eureka-service-consumer8082


        關(guān)鍵點(diǎn),使用SpringCloud的@LoadBalanced,才能調(diào)http://EUREKA-SERVICE-PROVIDER/api/users/? 接口的數(shù)據(jù),瀏覽器是不能直接調(diào)的


        import com.example.springcloud.ribbon.bean.User;
        import lombok.extern.slf4j.Slf4j;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
        import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
        import org.springframework.web.bind.annotation.*;
        import org.springframework.web.client.RestTemplate;

        import java.net.URI;

        @SpringBootApplication
        @EnableEurekaClient
        @RestController
        @Slf4j
        public class SpringcloudRibbonApplication {


        @Autowired
        RestTemplate restTemplate;

        public static void main(String[] args) {
        SpringApplication.run(SpringcloudRibbonApplication.class, args);
        }

        @GetMapping("/findUser/{username}")
        public User index(@PathVariable("username")String username){
        return restTemplate.getForObject("http://EUREKA-SERVICE-PROVIDER/api/users/"+username,User.class);
        }

        }

        6. 定制Netflix Ribbon client

        具體怎么定制?可以參考官網(wǎng),@RibbonClient指定定制的配置類既可

        package com.example.springcloud.ribbon.configuration;

        import com.example.springcloud.ribbon.component.MyRule;
        import com.netflix.loadbalancer.IPing;
        import com.netflix.loadbalancer.IRule;
        import com.netflix.loadbalancer.PingUrl;
        import org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;

        /**
        *

        * Ribbon Clients configuration
        *

        *
        *

        * @author mazq
        * 修改記錄
        * 修改后版本: 修改人:修改日期: 2020/07/29 14:22 修改內(nèi)容:
        *

        */

        //@Configuration(proxyBeanMethods = false)
        //@IgnoreComponentScan
        public class RibbonClientConfiguration {

        // @Autowired
        // IClientConfig config;

        @Bean
        public IRule roundRobinRule() {
        return new MyRule();
        }

        @Bean
        public ZonePreferenceServerListFilter serverListFilter() {
        ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
        filter.setZone("myTestZone");
        return filter;
        }

        @Bean
        public IPing ribbonPing() {
        return new PingUrl();
        }

        }

        在Application類加上@RibbonClient,name是為服務(wù)名稱,跟bootstrap.yml配置的一樣既可

        @RibbonClient(name = "eureka-service-provider",configuration = RibbonClientConfiguration.class)

        特別注意:官網(wǎng)這里特意提醒,這里的意思是說@RibbonClient指定的配置類必須加@Configuration(不過在Hoxton.SR6版本經(jīng)過我的驗(yàn)證,其實(shí)是可以不加的,加了反而可能報錯),@ComponentScan掃描要排除自定義的配置類,否則,它由所有@RibbonClients共享。如果你使用@ComponentScan(或@SpringBootApplication)

        其實(shí)就是想讓我們排除這個配置的全局掃描,所以我們可以進(jìn)行編碼,寫個注解類@IgnoreComponentScan,作用于類,指定@Target(ElementType.TYPE)

        package com.example.springcloud.ribbon.configuration;

        import java.lang.annotation.*;

        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        public @interface IgnoreComponentScan {

        }

        加上自定義的注解類

        任何在Application加上代碼,避免全局掃描:

        @ComponentScan(excludeFilters={@ComponentScan.Filter(type= FilterType.ANNOTATION,value= IgnoreComponentScan.class)})

        7. Netflix Ribbon常用組件

        ps:介紹Netflix Ribbon的負(fù)載策略之前,先介紹Netflix Ribbon常用組件及其作用:

        組件作用
        ILoadBalancer定義一系列的操作接口,比如選擇服務(wù)實(shí)例。
        IRule負(fù)載算法策略,內(nèi)置算法策略來為服務(wù)實(shí)例的選擇提供服務(wù)。
        ServerList負(fù)責(zé)服務(wù)實(shí)例信息的獲?。梢垣@取配置文件中的,也可以從注冊中心獲取。)
        ServerListFilter過濾掉某些不想要的服務(wù)實(shí)例信息。
        ServerListUpdater更新本地緩存的服務(wù)實(shí)例信息。
        IPing對已有的服務(wù)實(shí)例進(jìn)行可用性檢查,保證選擇的服務(wù)都是可用的。

        8. 定制Netflix Ribbon策略

        因?yàn)榉?wù)提供者是多實(shí)例的,所以再寫個接口測試,調(diào)用了哪個實(shí)例,來看看Netflix Ribbon的負(fù)載策略

        @Autowired
        LoadBalancerClient loadBalancerClient;

        @GetMapping(value = {"/test"})
        public String test(){
        ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKA-SERVICE-PROVIDER");
        URI uri = URI.create(String.format("http://%s:%s", serviceInstance.getHost() , serviceInstance.getPort()));
        System.out.println(uri.toString());
        return uri.toString();
        }

        部署成功,多次調(diào)用,可以看到每次調(diào)用的服務(wù)實(shí)例都不一樣?其實(shí)Netflix Ribbon默認(rèn)是按照輪詢的方式調(diào)用的

        要定制Netflix Ribbon的負(fù)載均衡策略,需要實(shí)現(xiàn)AbstractLoadBalancerRule抽象類,下面給出類圖:

        Netflix Ribbon內(nèi)置了如下的負(fù)載均衡策略,引用https://juejin.im/post/6854573215587500045的歸納:

        ok,接著我們可以在配置類,修改規(guī)則

        @Bean
        public IRule roundRobinRule() {
        return new BestAvailableRule();
        }

        測試,基本都是調(diào)8083這個實(shí)例,因?yàn)檫@個實(shí)例性能比較好

        顯然,也可以自己寫個策略類,代碼參考com.netflix.loadbalancer.RandomRule,網(wǎng)上也有很多例子,思路是修改RandomRule原來的策略,之前隨機(jī)調(diào)服務(wù)實(shí)例一次,現(xiàn)在改成每調(diào)5次后,再調(diào)其它的服務(wù)實(shí)例

        package com.example.springcloud.ribbon.component;

        import com.netflix.client.config.IClientConfig;
        import com.netflix.loadbalancer.AbstractLoadBalancerRule;
        import com.netflix.loadbalancer.ILoadBalancer;
        import com.netflix.loadbalancer.Server;

        import java.util.ArrayList;
        import java.util.List;
        import java.util.concurrent.ThreadLocalRandom;


        public class MyRule extends AbstractLoadBalancerRule
        {
        // 總共被調(diào)用的次數(shù),目前要求每臺被調(diào)用5次
        private int total = 0;
        // 當(dāng)前提供服務(wù)的機(jī)器號
        private int index = 0;

        public Server choose(ILoadBalancer lb, Object key)
        {
        if (lb == null) {
        return null;
        }
        Server server = null;

        while (server == null) {
        if (Thread.interrupted()) {
        return null;
        }
        // 獲取可用的服務(wù)列表
        List upList = lb.getReachableServers();
        // 獲取所有服務(wù)列表
        List allList = lb.getAllServers();

        int serverCount = allList.size();
        if (serverCount == 0) {
        // 沒有獲取到服務(wù)
        return null;
        }

        //int index = chooseRandomInt(serverCount);
        //server = upList.get(index);
        if(total < 5)
        {
        server = upList.get(index);
        total++;
        }else {
        total = 0;
        index++;
        if(index >= upList.size())
        {
        index = 0;
        }
        }

        if (server == null) {
        // 釋放線程
        Thread.yield();
        continue;
        }

        if (server.isAlive()) {
        return (server);
        }

        server = null;
        Thread.yield();
        }

        return server;
        }

        protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
        }

        @Override
        public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
        }

        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
        }
        }

        修改IRule ,返回MyRule

         @Bean
        public IRule roundRobinRule() {
        return new MyRule();
        }




        往期精彩推薦



        騰訊、阿里、滴滴后臺面試題匯總總結(jié) — (含答案)

        面試:史上最全多線程面試題 !

        最新阿里內(nèi)推Java后端面試題

        JVM難學(xué)?那是因?yàn)槟銢]認(rèn)真看完這篇文章


        END


        關(guān)注作者微信公眾號 —《JAVA爛豬皮》


        了解更多java后端架構(gòu)知識以及最新面試寶典


        你點(diǎn)的每個好看,我都認(rèn)真當(dāng)成了


        看完本文記得給作者點(diǎn)贊+在看哦~~~大家的支持,是作者源源不斷出文的動力


        作者:smileNicky

        出處:https://www.cnblogs.com/mzq123/p/13411111.html

        瀏覽 51
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一进一出下面喷白浆九瑶视频 | 国产精品96久久久久久 | 三级爱爱视频 | 欧美一级片在线 | 亚洲色五月天 | 国产精品久久久久久久浪潮网站 | 中文字幕第一页精品 | 国产精品92 | 性chinese国产freehd | 熟妇的大黑骚逼浪视频 |