Spring Cloud 学习笔记:Ribbon 负载均衡

Ribbon

Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。
Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon 客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出 Load Balancer 后面所有的机器,Ribbon 会自动的帮助基于某种规则(如简单轮询、随机连接等)去连接这些机器。我们很容易使用 Ribbon 实现自定义的负载均衡算法。
在这里插入图片描述
Ribbon 在工作的时候分成两步,第一步先选择 EurekaServer,它优先选择在同一个区域内负载较少的 server。第二步再依据用户指定的的策略,再从 server 取到的服务注册列表中选择一个地址。其中 Ribbon 提供了多种策略:比如轮询、随机和根据响应时间加权。
总之一句话: Ribbon 就是负载均衡 + RestTemplate 调用,最终实现RPC的远程调用。

RestTemplate 使用

Spring 框架提供的 RestTemplate 类可用于在应用中调用 REST 服务,它简化了与 http 服务的通信方式,统一了 RESTful 的标准,封装了http 链接,只需要传入 url 及返回值类型即可。相较于之前常用的 HttpClient,RestTemplate 是一种更优雅的调用 RESTful 服务的方式。

getForObject 方法

在这里插入图片描述

getForEntity 方法

在这里插入图片描述

cloud-consumer-order80.OrderController

@GetMapping("/consumer/payment/getforentiy/{id}")
    public CommonResult<Payment> getPayment2(@PathVariable("id") Long id){
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
        if(entity.getStatusCode().is2xxSuccessful()){
            log.info(entity.getStatusCode()+"\t"+entity.getHeaders());
            return entity.getBody();
        }else {
            return new CommonResult<>(444,"操作失败");
        }
    }

RestTemplate 调用成功
在这里插入图片描述

核心组件 IRule

根据特定算法从服务列表中选取一个要访问的服务
在这里插入图片描述

  • com.netflix.loadbalancer.RoundRobinRule:轮询
  • com.netflix.loadbalancer.RandomRule:随机
  • com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
  • WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
  • BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
  • AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
  • ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器

负载规则替换

在这里插入图片描述
规则:这个自定义配置类不能放在 @ComponentScan 所扫描的当前包下以及子包下,否则自定义的配置类就会被所有的 Ribbon 客户端所共享,达不到特殊化定制的目的了。而 @ComponentScan 所扫描的当前包下以及子包下则就是 Spring Boot 主程序所在的包下,因为 @SpringBootApplication 注解里就包含 @ComponentScan。
在这里插入图片描述
MySelfRule

/**
 * 自定义负载均衡规则类
 */
@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}

主启动类添加注解 @RibbonClient,在启动该微服务的时候就能去加载我们的自定义 Ribbon 配置类,从而使配置生效

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

注解中的微服务名称一定要和注册进 Eureka 中的微服务名称相同,大小写严谨
在这里插入图片描述

默认负载:轮询算法原理

在这里插入图片描述
源码 RoundRobinRule.java 中利用 CAS 自旋锁 实现

...
private int incrementAndGetModulo(int modulo) {
    int current;
    int next;
    do {
        current = this.nextServerCyclicCounter.get();
        next = (current + 1) % modulo;
    } while(!this.nextServerCyclicCounter.compareAndSet(current, next));

    return next;
}
...

手写轮询算法

在服务提供者端8001和8002的controller层添加方法

@GetMapping(value = "/payment/lb")
public String getPaymentLB(){
    return servicePort;
}

将80端的 applicationContextConfig 去掉 @LoadBalanced 注解

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

然后在当前包下创建lb包编写接口和实现类
在这里插入图片描述
LoadBanlancer.java

/**
 * 获取Eureka上的活着的服务总数
 */
public interface LoadBanlancer {
    ServiceInstance instance(List<ServiceInstance> serviceInstanceList);
}

MyLB.java

/**
 * 手写轮询算法
 */
@Component
public class MyLB implements LoadBanlancer {
    //原子类
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement(){
        int current;
        int next;
        //自旋锁 也可以用for(;;)
        do{
            current = this.atomicInteger.get();
            //超过最大值,为0,重新计数 2147483647 Integer.MAX_VALUE
            next = current >= 2147483647 ? 0 : current + 1;
        }while (!this.atomicInteger.compareAndSet(current,next));
        System.out.println("第"+next+"次访问,next:"+next);
        return next;
    }

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

修改 controller 层方法

@RestController
@Slf4j
public class OrderController {
//    private static final String PAYMENT_URL = "http://localhost:8001";
    private static final String PAYMENT_URL = "http://CLOUD-PROVIDER-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    /**
     * 自定义负载均衡规则
     */
    @Resource
    private LoadBanlancer loadBanlancer;
    @Resource
    private DiscoveryClient discoveryClient;

    /**
     * 路由规则:轮询
     * @return
     */
    @GetMapping(value = "/consumer/payment/lb")
    public String getPaymentLB(){
        List<ServiceInstance> instances = discoveryClient.getInstances("cloud-provider-service");
        if(instances == null || instances.size() <= 0){
            return null;
        }
        ServiceInstance serviceInstance = loadBanlancer.instance(instances);
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri+"/payment/lb",String.class);
    }
}

启动微服务并访问相应 url
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

Ribbon 和 Nginx 区别

Ribbon 本地负载均衡客户端和 Nginx 服务端负载均衡区别:
Ribbon 本地负载均衡是在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到 JVM 本地,从而在本地实现 RPC 远程服务调用技术。
Nginx 是服务器负载均衡,客户端所有请求都会交给 nginx ,然后由 nginx 实现转发请求。即负载均衡是由服务端实现的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值