SpringCloud-Alibaba之负载均衡Ribbon

本文详细介绍了SpringCloud中Ribbon的作用,包括服务调用的简化和负载均衡策略,如轮询、随机、权重等,并演示了自定义负载均衡规则和请求重试机制。通过实例演示了Ribbon如何从Eureka获取服务列表并进行智能路由。

此文章主要讲解springcloud中负责服务调用和负载均衡的工具Ribbon的相关知识。

前言

前面的章节中,我们已经实现了服务的注册和服务发现。当启动某个服务的时候,可以通过HTTP的形式将信息注册到注册中心,并且可以通过SpringCloud提供的工具获取注册中心的服务列表。但是服务之间的调用还存在很多问题:如何更加方便的调用微服务,多个微服务的提供者如何选择,如何负载均衡等等。

Ribbon基础

什么是Ribbon?

  • Ribbon是Netflix公司发布的一个负载均衡器,有助于控制HTTP和TCP的客户端行为。在SpringCloud中,Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon利用从Eureka中读取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载均衡。

  • 在SpringCloud中可以将注册中心Eureka和Ribbon配合使用,Ribbon自动的从注册中心获取服务提供者的列表新,并基于内置的负载均衡算法,请求服务。

Ribbon的作用

  • 服务调用:基于Ribbon实现服务调用,是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。借助于RestTemplate最终进行调用

  • 负载均衡:当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址。

简介

Github地址

1597384916817

1597385089429

1597385250463

1597385267753

Ribbon的使用

我们这里提前启动好之前在搭建的 eureka Server 集群(5个模块)

  • cloud-eureka-server7001
  • cloud-eureka-server7002
  • cloud-provider-payment8001
  • cloud-provider-payment8002
  • cloud-consumer-order80

image-20210117111350254

上面在eureka时,确实实现了负载均衡机制,那是因为 netflix-eureka-client 包里面自带着 ribbon,在使用Eureka作为注册中心的时候,不需要再导入Ribbon的相关依赖,因为Eureka内部集成了Ribbon了。

image-20210117110955152

RestTemplate 说明

有两种请求方式:post和get ,还有两种返回类型:object 和 Entity

1597385762892

RestTemplate 的 ForEntity 相比 ForObject特殊的地方:

1597385959115

1597385892918

就是说如果使用 ForObject 得到的就是提供者返回的对象,而如果要使用 ForEntity 得到时 ResponstEntity 对象,使用 getBody() 才能得到提供者返回的数据。

//使用forEnriry示例:
@GetMapping("/consumer/payment/forEntity/{id}")
public CommonResult<Payment> getPaymentById2(@PathVariable("id") Long id) {

    ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/" + id, CommonResult.class);

    if (entity.getStatusCode().is2xxSuccessful()) {
        return entity.getBody();
    } else {
        return new CommonResult<>(444, "操作失败");
    }
}

负载均衡

什么是负载均衡
  • 在搭建网站的时候,如果单节点的web服务性能和可靠性都无法达到要求;或者是在使用外网服务的时候,经常担心被人攻破,一不小心就会有打开外网端口的请求,通常这个时候加入负载均衡就能有效解决服务问题。

  • 负载均衡是一种基础的网络服务,其原理是通过运行在前面的负载均衡服务,按照指定的负载均衡算法,将流量分配到后端服务集群上,从而为系统提供并行扩展的能力。

  • 负载均衡的应用场景包括流量包、转发规则以及后端服务,由于该服务有内外网个例、健康检查等功能,能够有效提供系统的安全性和可用性。

image-20210117122157989

客户端负载均衡和服务端负载均衡

客户端负载均衡

  • 客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,进行访问;即在客户端进行负载均衡算法分配。

  • 典型应用:Ribbon是客户端负载均衡。

服务端负载均衡

  • 先发送请求到负载均衡服务器或软件,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端进行负载均衡算法分配。

  • 典型应用:

  • 硬件:F5等。

  • 软件:Nginx等。

Ribbon内置的负载均衡策略

Ribbon内置了多种负载均衡策略,内部负责复杂均衡的顶层接口为com.netflix.loadbalancer.IRule,实现方式如下:

image-20210117122428201

Ribbon 负载均衡规则类型

IRule接口的实现)

1597386516205

com.netflix.loadbalancer.RoundRobinRule:# 以轮询的方式进行负载均衡。
com.netflix.loadbalancer.RandomRule:# 随机策略。
com.netflix.loadbalancer.RetryRule:# 重试策略。
com.netflix.loadbalancer.WeightedResponseTimeRule:# 权重策略。会计算每个服务的权重,权重越高的被调用的可能性会越大。
com.netflix.loadbalancer.BestAvailableRule:# 最佳策略。遍历所有的服务实例,过滤掉故障实例,并将请求数量最小的实例返回。
com.netflix.loadbalancer.AvailabilityFilteringRule:# 可用过滤策略。过滤掉故障和请求数超过阈值的服务实例,再从剩下的实例中轮询调用。

配置负载均衡规则

Ribbon内置的负载均衡策略的使用一

在服务消费者的application.yml中修改负载均衡策略

# 修改ribbon的负载均衡策略 服务名 - ribbon - NFLoadBalancerRuleClassName :负载均衡策略
service-product:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 修改ribbon的负载均衡策略为权重策略
Ribbon内置的负载均衡策略的使用二

1597386638507

注意上面说的,而Springboot主启动类上的 @SpringBootApplication 注解,相当于加了@ComponentScan注解,会自动扫描当前包及子包,所以注意不要放在SpringBoot主启动类的包内。

修改 cloud-consumer-order80,新建包 myrule

image-20210117120449623

在这个包下新建 MySelfRule类

@Configuration
public class MySelfRule {
    @Bean
    public IRule myrule(){
        return new RandomRule(); //负载均衡规则定义为随机
    }
}

然后在主启动类上添加注解 @RibbonClient

@EnableEurekaClient
@SpringBootApplication
//指定该负载均衡规则对哪个提供者服务使用    加载自定义规则的配置类
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}

测试访问 http://localhost/consumer/payment/1 ,查看结果

Ribbon的策略选择

  • 如果每个机器配置一样,建议不修改策略(推荐)。

  • 如果部分机器配置强,则可以改为WeightedResponseTimeRule。

请求重试机制

在实际生产环境中,Ribbon做客户端负载均衡的时候,Ribbon默认的负载均衡算法是轮询,一旦访问到的那台微服务提供者突然宕机了,此时就会出现404的情况,这时可以使用Ribbon的请求重试机制,Ribbon的请求重试机制基于Spring的retry(Spring的重试框架)。

使用

在微服务消费者导入spring-retry的Maven坐标:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

修改微服务消费者的application.yml:

# Ribbon的重试机制
service-product:
  ribbon:
#    修改ribbon的负载均衡策略 服务名 - ribbon - NFLoadBalancerRuleClassName :负载均衡策略
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 修改ribbon的负载均衡策略为权重策略
    # Ribbon的重试机制参数
    ConnectTimeout: 250 # Ribbon的连接超时时间
    ReadTimeout: 1000 # Ribbon的数据读取超时时间
    OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
    MaxAutoRetriesNextServer: 2 # 切换实例的重试次数
    MaxAutoRetries: 1 # 对当前实例的重试次数

Ribbon中负载均衡的源码分析

参考文章:https://www.yuque.com/sunxiaping/yg511q/kxmrqz#b7025e02

轮询算法原理

1597387609476

手写负载算法

原理+JUC(CAS+自旋锁的复习)

启动 Eureka集群,7001和7002

8001和8002微服务改造

在这两个提供者的Controller中加入以下代码,便于测试

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

服务提供者80改造

配置类

去掉 @LoadBalanced 注解

LoadBalancer 接口
package com.itjing.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;

import java.util.List;

public interface LoadBalancer {
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
LoadBalancer 接口实现类
package com.itjing.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class MyLB implements LoadBalancer {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

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

    // 负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启后rest接口计数从1开始
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}
OrderController

加入以下内容

@Resource
private LoadBalancer loadBalancer;

@Resource
private DiscoveryClient discoveryClient;

@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB() {
    List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
    if (instances == null || instances.size() <= 0) {
        return null;
    }

    ServiceInstance serviceInstance = loadBalancer.instances(instances);
    URI uri = serviceInstance.getUri();
    return restTemplate.getForObject(uri + "/payment/lb", String.class);
}
启动访问测试

http://localhost/consumer/payment/lb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱晶哥哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值