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


击石乃有火,不击元无烟!!
原文地址:https://www.cnblogs.com/rain2020/p/13511573.html