Spring Cloud - 02 (Ribbon、Feign)

负载均衡-Ribbon

客户端负载均衡

相比较于Nginx的服务端负载均衡,Ribbon属于客户端的负载均衡。客户端负载均衡和服务器负载均衡的核心差异在服务列表本身,客户端负载均衡服务列表是通过客户端自己维护的,而服务端负载均衡服务列表是由中间服务(例如Nginx)单独维护。Ribbon通过Eureka拿到所有服务列表,然后通过自身的负载均衡策略,选择一个服务去处理请求。

负载均衡策略

IRule:Ribbon所有的负载均衡策略类都需实现该接口。

策略类 命名 说明
RandomRule 随机策略 随机选择 Server
RoundRobinRule 轮训策略 按顺序循环选择 Server
RetryRule 重试策略 在一个配置时问段内当选择 Server 不成功,则一直尝试选择一个可用的 Server,超过时间则返回null
BestAvailableRule 最低并发策略 先过滤掉故障服务,然后它会基于过去30分钟的统计结果选取当前并发量最小的服务节点,也就是最“闲”的节点作为目标地址。如果统计结果尚未生成,则采用轮询的方式选定节点。
AvailabilityFilteringRule 可用过滤策略 过滤掉一直连接失败并被标记为 circuit tripped 的 Server,过滤掉那些高并发连接的 Server(active connections 超过配置的网值)
ResponseTimeWeightedRule 响应时间加权策略 根据 Server 的响应时间分配权重。响应时间越长,权重越低,被选择到的概率就越低;响应时间越短,权重越高,被选择到的概率就越高。这个策略很贴切,综合了各种因素,如:网络、磁盘、IO等,这些因素直接影响着响应时间
ZoneAvoidanceRule 区域权衡策略 综合判断 Server 所在区域的性能和 Server 的可用性轮询选择 Server,并且判定一个 AWS Zone 的运行性能是否可用,剔除不可用的 Zone 中的所有 Server

在代码中使用 Ribbon

配置负载均衡策略

  • 全局的配置方式,针对所有服务生效,添加Configuration配置类就行了

    import com.netflix.loadbalancer.IRule;
    import com.netflix.loadbalancer.RandomRule;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class RibbonConfiguration {
    
    	/**
    	 * 负载均衡策略-随机
    	 * @return
    	 */
    	@Bean
    	public IRule defaultLBStrategy() {
    		return new RandomRule();
    	}
    }
    
  • 局部的配置方式,正对指定的服务生效,有两种方式:

    • 配置文件配置:

      # 注意eureka-client是所针对的服务的名称
      eureka-client.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
      
    • 通过注解配置 @RibbonClient:

      import org.springframework.cloud.netflix.ribbon.RibbonClient;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      @RibbonClient(name = "eureka-client", configuration = com.netflix.loadbalancer.RandomRule.class)
      public class RibbonConfiguration {
      
      }
      

@LoadBalanced 注解的使用

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonConsumerApplication {

    @Bean
    @LoadBalanced
    public RestTemplate template() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(RibbonConsumerApplication.class, args);
    }
}

代码中使用RestTemplate发送请求,则可以在RestTemplate的配置方法上加上@LoadBalanced注解,这样当RestTemplate发送请求时将会使用Ribbon的负载均衡。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class Controller {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("sayHi")
    public String sayHi() {
		// 注意这里的请求地址不需要指明服务地址,只需要指明服务名称即可
		// 这里就是负载均衡eureka-client服务,调用sayHi接口
        return restTemplate.getForObject("http://eureka-client/sayHi", String.class);
    }
}

IPing

IPing是Ribbon的一套healthcheck机制,故名思议,就是要Ping一下目标机器看是否还在线,一般情况下IPing并不会主动向服务节点发起healthcheck请求,Ribbon后台通过静默处理返回true默认表示所有服务节点都处于存活状态(和Eureka集成的时候会检查服节点UP状态)。


Ribbon的懒加载和饥饿加载

Ribbon默认是懒加载模式,首次加载发生在第一次调用的时候。
Ribbon还有一种加载模式是饥饿加载,会在项目启动时加载,需要添加配置:

# 开启Ribbon的饥饿加载模式,默认是懒加载(调用时才会初始化LoadBalancer)
ribbon.eager-load.enabled=true
# 指定需要应用饥饿加载的服务名称
ribbon.eager-load.clients=ribbon-consumer

服务间的通讯与调用-Feign

spring cloud服务之间的调用是基于HTTP的,然而对于原生的HTTP调用来说是件非常麻烦的事。Feign的出现就是为了解决这个问题,我们可以借助Feign的代理机制,像调用一个接口方法一样发起远程HTTP调用。

老的服务间调用方式:

@RestController
public class Controller {

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/hello")
    public String hello() {
        // 获取eureka-client服务实例
        ServiceInstance instance = loadBalancerClient.choose("eureka-client");
        //调用eureka-client服务的sayHi接口
        String target = String.format("http://%s:%s/sayHi", instance.getHost(), instance.getPort());
        return restTemplate.getForObject(target, String.class);
    }
}

使用feign后的调用方式:

@RestController
public class Controller {

    @Autowired
    private IService iService;

    @GetMapping("hello")
    public String hello() {
        return iService.sayHi();
    }
}

feign代码实践

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-demo</artifactId>
        <groupId>com.jinsh</groupId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>feign-consumer</artifactId>
    <packaging>jar</packaging>
    <name>feign-consumer</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

启动类加@EnableFeignClients注解,配置文件还和普通服务注册配置文件一样

package com.jinsh.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class FeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignApplication.class, args);
    }
}

IService 接口,添加@FeignClient("eureka-client")注解,eureka-client是想要调用的服务名称

package com.jinsh.springcloud;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient("eureka-client")
public interface IService {

    @GetMapping("sayHi")
    String sayHi();
}

controller中调用eureka-client服务的sayHi接口:

package com.jinsh.springcloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Controller {

    @Autowired
    private IService iService;

    @GetMapping("sayHi")
    public String sayHi() {
        return iService.sayHi();
    }
}

feign使用讲解

引用

feign 中是包含ribbon和hystrix的,所以引用了feign后就不需要单独引用ribbon和hystrix了。

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

注解

1、@EnableFeignClients
需要在消费者的服务启动类上使用@EnableFeignClients注解,消费者服务才能以feign方式调用其他服务接口,
如果被调用的服务接口包名与消费者服务包名不同,例如消费者服务的包名为com.jinsh.consumer,被调用方的服务包名为com.jinsh.client,那么EnableFeignClients注解上还需要添加被调用服务的包名@EnableFeignClients(basePackages = {"com.jinsh.client"})

2、@FeignClient
FeignClient注解添加在接口类上,注解上还需要声明被调用的服务名@FeignClient("feign-client"),接口内则定义被调用服务的方法。

feign 的超时重试机制

feign 的超时重试机制底层是通过ribbon实现的。下面是配置超时重试的参数:

# feign-service-provider是服务提供者的服务名
feign-service-provider.ribbon.OkToRetryOnAllOperations=true
feign-service-provider.ribbon.ConnectTimeout=1000
feign-service-provider.ribbon.ReadTimeout=2000
feign-service-provider.ribbon.MaxAutoRetries=2
feign-service-provider.ribbon.MaxAutoRetriesNextServer=2
  • OkToRetryOnAllOperations:这个参数指定了什么HTTP Method可以进行Retry,这里为了演示方便才设置为true,表示不管GET还是POST什么都能重试。真实的生产环境往往只是GET请求可以重试,或者实现了幂等性的其他类型请求。

  • ConnectTimeout:超时判定的第一个参数(单位ms),创建会话的连接时间。注意,这个不是服务的响应时间,而是本机和服务建立一个Connection所花费的时间,如果连接超时则直接进行重试。

  • ReadTimeout:超时判定的第二个参数,服务响应时间。当连接建立好之后,如果对方服务没有在规定时间内返回,则直接进行重试。

  • MaxAutoRetries:当前节点重试次数。这里重试次数为2,那么在首次调用超时以后,会再次向同一个服务节点发起最多2次重试(总共向当前节点1+2=3次请求)。

  • MaxAutoRetriesNextServer:换N个节点重试。这里N=2,就是说在当前机器调用超时后,Feign将最多换N台机器发起调用(注意,这里将和第一个参数共同作用,也就是说,在新机器上超时后,会继续重试MaxAutoRetries+1次)。

根据上面的配置,我们可以计算一次请求的最大超时时间

(2000 + 1000)*(2 + 1)*(2 + 1)= 27000ms

Spring Cloud - 02 (Ribbon、Feign)

上一篇:vscode调试python时提示无法将“conda”项识别为 cmdlet、函数、脚本文件或可运行程序的名称的解决方法


下一篇:linux安装python