LoadBalance-Ribbon-Feign

理论

原文
另外一篇原理
负载均衡-LVS、HAProxy、Nginx、F5

1 负载均衡产生的背景

LB(Load Balance,负载均衡)是一种集群技术,它将特定的业务(网络服务、网络流量等)分担给多台网络设备(包括服务器、防火墙等)或多条链路,从而提高了业务处理能力,保证了业务的高可靠性。

负载均衡技术具有一下优势:

(1)高性能:负载均衡技术将业务较均衡的分担到多台设备或链路上,从而提高了整个系统的性能;

(2)可扩展性:负载均衡技术可以方便的增加集群中设备或链路的数量,在不降低业务质量的前提下满足不断增长的业务需求;

(3)高可靠性:单个甚至多个设备或链路法神故障也不会导致业务中断,提高了整个系统的可靠性;

(4)可管理性:大量的管理共组都集中在使用负载均衡技术的设备上,设备集群或链路集群只需要维护通过的配置即可;

(5)透明性:对用户而言,集群等于一个或多个高可靠性、高性能的设备或链路,用户感知不到,也不关心具体的网络结构,增加或减少设备或链路数量都不会影响正常的业务。

负载均衡技术分类:

(1) 服务器负载均衡:在数据中心等组网环境中,可以采用服务器负载均衡,将网络服务分担给多台服务器进行处理,提高数据中心的业务处理能力;

(2) 链路负载均衡:在有多个运营商出接口的组网环境中,可以采用出方向多链路动态负载均衡,实现链路的动态选择,提高服务的可靠性;

(3) 防火墙负载均衡:在防火墙处理能力成为瓶颈的组网环境中,可以采用防火墙负载均衡,将网络流量分担给多台防火墙设备,提高防火桥的处理能力;

Ribbon

Ribbon+RestTemplate调用

  1. 是什么?
    Spring Cloud Ribbon主要功能是提供 客户端的软件负载均衡算法和服务调用 。

Ribbon客户端组件提供一系列完善的配置项,如连接超时,重试等。 就是在配置文件中列出 Load Balance 后面所有机器,Ribbon会自动帮助你基于某种规则 (如简单轮询,随机连接等)去连接这些机器。

我们很容易使用Ribbon实现自定义的负载均衡算法。

  1. 负载均衡区别
    负载均衡(Load Balance)是将用户的请求平摊的分配到多个服务上,从而达到HA(高可用),常见的负载均衡有 Nginx,LVS,硬件F5等。

本地负载均衡vs 服务器负载均衡
Ribbon 是本地负载均衡,客户端发来的请求由Ribbon统一决定发往那个服务端的微服务。在微服务调用接口时,在注册中心上获取注册信息服务列表之后缓存在JVM本地,从而实现本地RPC远程服务调用技术。

Nginx 是服务端负载均衡:客户端所有请求都会交给nginx,然后由nginx实现请求转发,即负载均衡是由服务端实现的。

集中式负载均衡vs进程内负载均衡

目前业界主流的负载均衡方案可分成两类:

第一类:集中式负载均衡, 即在consumer和provider之间使用独立的负载均衡设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把 访问请求 通过某种策略转发至provider;

第二类:进程内负载均衡,将负载均衡逻辑集成到consumer,consumer从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的provider。
Ribbon就属于后者,它只是一个类库,集成于consumer进程,consumer通过它来获取到provider的地址。

demo

1、服务发现的 jar 中已经包含了负载均衡的ribbon和loadbalance,无需引入依赖。
2、在cloud-consumer-order80服务的主启动类上加上@RibbonClient
@RibbonClient(name = “CLOUD-PAYMENT-SERVICE”,configuration=MySelfRule.class)
name=“提供提供服务的微服务名称”,configuration=负载均衡算法
3、然后写一个Config,加上 @LoadBalanced

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

4、Controller层:

    @Resource
    private RestTemplate restTemplate;

    @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);

    }

Ribbon工作时有两步:
先选择 EurekaServer,优先选择统一区域负载较少的 server
再根据用户指定的策略,从server取到的服务注册列表中选择一个地址。

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

Ribbon 重试机制
在集群环境中,用多个节点来提供服务,难免会有某个节点出现故障。用 Nginx 做负载均衡的时候,如果你的应用是无状态的、可以滚动发布的,也就是需要一台台去重启应用,这样对用户的影响其实是比较小的,因为 Nginx 在转发请求失败后会重新将该请求转发到别的实例上去。

由于 Eureka 是基于 AP 原则构建的,牺牲了数据的一致性,每个 Eureka 服务都会保存注册的服务信息,当注册的客户端与 Eureka 的心跳无法保持时,有可能是网络原因,也有可能是服务挂掉了。

在这种情况下,Eureka 中还会在一段时间内保存注册信息。这个时候客户端就有可能拿到已经挂掉了的服务信息,故 Ribbon 就有可能拿到已经失效了的服务信息,这样就会导致发生失败的请求。

这种问题我们可以利用重试机制来避免。重试机制就是当 Ribbon 发现请求的服务不可到达时,重新请求另外的服务。

# 对当前实例的重试次数
ribbon.maxAutoRetries=1
# 切换实例的重试次数
ribbon.maxAutoRetriesNextServer=3
# 对所有操作请求都进行重试
ribbon.okToRetryOnAllOperations=true
# 对Http响应码进行重试
ribbon.retryableStatusCodes=500,404,502

在这里插入图片描述

Feign

目前,在Spring cloud 中服务之间通过restful方式调用有两种方式

  • restTemplate+Ribbon
  • feign

怎么理解Feign和Ribbon及二者的区别?
feign是声明式的web service客户端,记住是客户端微服务,它让微服务之间的调用变得类似controller调用service。Spring Cloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。
Spring Cloud Netflix 的微服务都是以 HTTP 接口的形式暴露的,所以可以用 Apache 的 HttpClient 或 Spring 的 RestTemplate 去調用

而 Feign 是一個使用起來更加方便的 HTTP 客戶端,它用起來就好像調用本地方法一樣,完全感覺不到是調用的遠程方法

发布到注册中心的服务方接口,是 HTTP 的,也可以不用 Ribbon 或者 Feign,直接浏览器一样能够访问

只不过 Ribbon 或者 Feign 调用起来要方便一些,最重要的是:它俩都支持软负载均衡

注意:spring-cloud-starter-feign 里面已经包含了 spring-cloud-starter-ribbon(Feign 中也使用了 Ribbon)
从实践上看,采用feign的方式更优雅(feign内部也使用了ribbon做负载均衡)。

OpenFeign

Feign是声明性Web服务客户端。它使编写Web服务客户端更加容易。 要使用Feign,请创建一个接口并对其进行注释。 它具有可插入的注释支持,包括Feign注释和JAX-RS注释。 Feign还支持可插拔编码器和解码器。 Spring Cloud添加了对Spring MVC注释的支持,并支持使用HttpMessageConvertersSpring Web中默认使用的注释。Spring Cloud集成了Ribbon和Eureka以及Spring Cloud LoadBalancer,以在使用Feign时提供负载平衡的http客户端

  1. 作用
    声明式方式定义Web服务客户端;
    通过集成Ribbon或Eureka实现负载均衡的HTTP客户端。
    就是我们不用再去写RestTemplate 调用微服务接口了,直接用接口调用就可以了

1.1 与Feign 区别
Feign是Springcloud组件中的一个轻量级Restful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务
OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

demo

新建cloud-consumer-feign-order80工程

 <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--需要Ribbon的jar包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000

主启动类:

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

现在要调用cloud-provider-payment8001/8002接口,那么其业务逻辑接口暴露什么,我就调用什么。
cloud-provider-payment8001的service接口:

public interface PaymentService
{
   public int create(Payment payment);
   public Payment getPaymentById(@Param("id") Long id);
}

则cloud-consumer-feign-order80对应的service接口如下,填入cloud-provider-payment8001的create方法的方法签名

@service
//Controller层的请求过来了后,就去注册中心找名字叫CLOUD-PAYMENT-SERVICE的服务(即所有的provider-payment服务)
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService
{
     @PostMapping(value = "/payment/create")
    public CommonResult create(Payment payment);
    
    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}

cloud-consumer-feign-order80的Controller:

@RestController
@Slf4j
public class OrderFeignController
{
    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping(value = "/consumer/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
    {
        return paymentFeignService.getPaymentById(id);
    }

    @GetMapping(value = "/consumer/payment/feign/timeout")
    public String paymentFeignTimeout()
    {
        // OpenFeign客户端一般默认等待1秒钟
        return paymentFeignService.paymentFeignTimeout();
    }
}

在这里插入图片描述
调用流程原理:
在这里插入图片描述
测试:
在这里插入图片描述

超时控制

Feign底层用Ribbon实现负载均衡,所以使用的是Ribbon的超时控制,默认等待1s。
测试超时:
在PaymentFeignService下添加

  @GetMapping(value = "/payment/feign/timeout")
    String paymentFeignTimeout();

在cloud-provider-payment8001的Controller中添加下面的方法,故意超时3s

@GetMapping(value = "/payment/feign/timeout")
    public String paymentFeignTimeout()
    {
        // 业务逻辑处理正确,但是需要耗费3秒钟
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        return serverPort;
    }

测试超时:
在这里插入图片描述

可以通过配置文件自定义超时时间,如下:

#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值