服务调用 Ribbon

Spring Cloud Ribbon是Netflix Ribbon实现的一套客户端,主要功能是提供客户端软件负载均衡算法和服务调用。Ribbon客户端提供一系列完善的配置如连接超时,重试等。就是在配置文件中列出Load Balancer后面的所有机器,Ribbon会自动帮你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们能很容易的使用Ribbon实现自定义的负载均衡算法。

Ribbon负载均衡和Rest调用:

1.Eureka客户端或者Feign的依赖中已经引入Ribbon的依赖(也可单独引入)

2.配置类中给容器注入RestTemplate

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

3.在Controller层注入RestTemplate

@Resource
private RestTemplate restTemplate;
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

4.调用服务方法

restTemplate.getForObject(url, responseType);
restTemplate.postForObject(url, request, responseType);

 Ribbon负载均衡:(默认负载均衡策略是轮询)

1.添加负载均衡策略的配置类

@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        new RoundRobinRule(); // 轮询,默认的负载均衡策略
        new RandomRule(); // 随机获取服务
        new RetryRule(); // 按照轮询获取服务,指定时间内失败会重试
        new WeightedResponseTimeRule(); // 按权重选择服务
        new BestAvailableRule(); // 先过滤不可用服务,然后选择并发量最小的服务
        new AvailabilityFilteringRule(); // 先过滤故障实例,再选择并发较小的实例
        new ZoneAvoidanceRule(); // 综合判断server所在区域的性能,和server的可用性
        return new MyRule();
    }
}

注意:Ribbon的负载均衡的配置类不能放再主启动类所在的包或子包

 2.在主启动类上添加 @RibbonClient 注解

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}

注意:@RibbonClient 的name属性指定的服务名需要与注册中心中的服务名称一致(大写)

自定义轮询策略方式一

1.新建一个轮询规则类继承 AbstractLoadBalancerRule 类去重新 choose() 方法

@Slf4j
public class MyRule2 extends AbstractLoadBalancerRule {
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }

    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        Server server = null;
        if (lb != null) {
            List<Server> allServers = lb.getAllServers();
            int serverCount = allServers.size();
            if (serverCount != 0) {
                int nextServerIndex = incrementAndGetModulo(serverCount);
                server = (Server) allServers.get(nextServerIndex);
            }
        }
        return server;
    }

    // 自旋锁获取服务列表的下标值
    private int incrementAndGetModulo(int serverCount) {
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = (current + 1) % serverCount;
        } while(!this.atomicInteger.compareAndSet(current, next));return next;
    }
}

2.在Ribbon的负载均衡配置类中注入IRule时候使用刚刚自定义的类

@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        return new MyRule2();
    }
}

自定义轮询策略方式二

1.定义LoadBalancer接口

public interface LoadBanlacer {
    ServiceInstance getInstance(List<ServiceInstance> serviceInstances);
}

2.定义MyLB类实现LoadBalancer接口实现getInstance()方法

@Component
public class MyLB implements LoadBanlacer {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement() {
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = current >= Integer.MAX_VALUE ? 0 : current + 1;
        } while (!this.atomicInteger.compareAndSet(current, next));
        System.out.println("***访问次数****next="+next);
        return next;
    }

    @Override
    public ServiceInstance getInstance(List<ServiceInstance> serviceInstances) {
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

3.注入 LoadBalancer 与 DiscoveryClient 对象

 4.调用服务

@GetMapping("/consumer/payment/lb")
    public String getPaymentLB(){
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        if (instances == null || instances.size() <= 0) {
            return null;
        }
        ServiceInstance instance = loadBanlacer.getInstance(instances);
        URI uri = instance.getUri();
        return restTemplate.getForObject(uri+"/payment/lb", String.class);
    }
posted @ 2021-06-09 16:50  一柒微笑  阅读(178)  评论(0编辑  收藏  举报