zoukankan      html  css  js  c++  java
  • SpringCloud-03-服务消费端负载均衡Feign/Ribbon


    在服务消费端进行负载均衡

    Ribbon和Eureka整合以后,客户端可以直接调用,不用关心IP地址和端口

    Ribbon做两件事情

    找Eureka查询可用的服务列表(Eureka是集群,随便挂几台没事)
    通过负载均衡机制向服务提供者调用服务(服务提供者也是集群,随便挂几台没事;可用性高,一致性不强)

    服务消费端改造

    依赖

    <!--ribbon-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-ribbon</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!-- eureka  -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>

    配置

    eureka:
      client:
        register-with-eureka: false #laozi不是要去向eureka中注册自己
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

    开启注解

    @SpringBootApplication
    @EnableEurekaClient
    public class DeptConsumer_80 {
        public static void main(String[] args) {
            SpringApplication.run(DeptConsumer_80.class,args);
        }
    }

    配置类

    @Configuration//就相当于spring的applicationContext.xml
    public class ConfigBean {
    
        @Bean
        @LoadBalanced//配置ribbon负载均衡实现RestTemplate
        public RestTemplate getTemplate(){
            return new RestTemplate();
        }
    }

    调用的地方需要改造

    @RestController
    public class DeptConsumerController {
    
        //RestTemplate
        @Autowired
        private RestTemplate restTemplate;//提供多种便捷访问远程http服务的方法,简单的restful服务模板
    
    //    private static final String REST_URL_PREFIX = "http://localhost:8001";
        //ribbon去实现时 地址应该是个变量  通过服务名访问
        private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
    
        @RequestMapping("/consumer/dept/get/{id}")
        public Dept get(@PathVariable("id") Long id){
            return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
        }
    
        @RequestMapping("/consumer/dept/add")
        public boolean add(Dept dept){
            return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add/",dept,Boolean.class);
        }
    
        @RequestMapping("/consumer/dept/list")
        public List<Dept> list(){
            return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list/", List.class);
        }
    }

    Eureka注册中心改造

    搞成集群形式


    服务提供者改造

    搞成集群形式,都注册到注册中心


    负载均衡算法自定义

    回到这个配置类

    @Configuration//就相当于spring的applicationContext.xml
    public class ConfigBean {
    
        //IRule接口的实现类:
        //AvailabilityFilteringRule:先过滤掉跳闸的服务 对剩下的服务进行轮询
        //RoundRobinRule:轮询  默认使用这个
        //RandomRule:随机
        //RetryRule:先轮询,失败的话,在指定的时间内重试
        @Bean
        @LoadBalanced//配置ribbon负载均衡实现RestTemplate
        public RestTemplate getTemplate(){
            return new RestTemplate();
        }
    }

    该配置类使程序根据负载均衡策略请求服务

    如何自定义负载均衡的策略?

    https://www.springcloud.cc/spring-cloud-netflix.html

    如上图指示的方式自定义配置类

    在启动类指定自定义配置类

    @SpringBootApplication
    @EnableEurekaClient
    //在微服务启动的时候就去加载 自定义的Ribbon类
    @RibbonClient(name="SPRINGCLOUD-PROVIDER-DEPT",configuration = RefrainRule.class)
    public class DeptConsumer_80 {
        public static void main(String[] args) {
            SpringApplication.run(DeptConsumer_80.class,args);
        }
    }

    自定义配置类

    @Configuration
    public class RefrainRule {
        @Bean
        public IRule myRule(){
            return new RefrainRandomRule();//自定义负载均衡策略
        }
    }

    具体算法(照猫画虎;算法有缺陷,为了说明问题)

    public class RefrainRandomRule extends AbstractLoadBalancerRule {
    
        //每个服务,访问5次,换下一个服务
        //total=0  如果=5  指向下一个服务节点
        //total=5  index+1
    
        private int total = 0;//被调用的次数
        private int currentIndex = 0;//当前是谁在提供服务
    
        //@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
        public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                return null;
            }
            Server server = null;
    
            while (server == null) {
                if (Thread.interrupted()) {
                    return null;
                }
                List<Server> upList = lb.getReachableServers();//获得活着的服务
                List<Server> allList = lb.getAllServers();//获得全部的服务
    
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }
    
    //            int index = chooseRandomInt(serverCount);//生成区间随机数
    //            server = upList.get(index);//从活着的服务中随机获取一个
    
                //===============================================================================
    
                if(total<5){
                    server = upList.get(currentIndex);
                    total++;
                }else{
                    total = 0;
                    currentIndex++;
                    if(currentIndex>upList.size()){
                        currentIndex = 0;
                    }
                    upList.get(currentIndex);
                }
                //===============================================================================
    
                if (server == null) {
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive()) {
                    return (server);
                }
    
                // Shouldn't actually happen.. but must be transient or a bug.
                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) {
            // TODO Auto-generated method stub
            
        }
    }

    Feign

    社区里的大佬觉着Ribbon是以服务名的方式进行调用的,不符合Java面向接口编程的习惯,于是搞了一套“接口+注解”形式的调用方式。

    原(springcloud-consumer-dept-80)

    新(springcloud-consumer-dept-feign)

    (springcloud-api)

    示例跑成功了,但是当真没咋看懂,为啥要去(springcloud-api)这个工程里边绕一圈丶


    https://github.com/ChenCurry/springcloud.git


    击石乃有火,不击元无烟!!
  • 相关阅读:
    Timer定时任务
    spring boot配置多数据源
    消费者模块调用提供者集群报错
    修改windHost文件
    spring常用注解+Aop
    添加ClustrMaps
    无题
    2020年3月21日 ICPC训练联盟周赛,Benelux Algorithm Programming Contest 2019
    2020年3月14日 ICPC训练联盟周赛,Preliminaries for Benelux Algorithm Programming Contest 2019
    2020.4.12 个人rating赛 解题+补题报告
  • 原文地址:https://www.cnblogs.com/rain2020/p/13511573.html
Copyright © 2011-2022 走看看