3Ribbon客户端 · SpringCloud微服务实战 · 看云

导航


本节代码地址


下面我们开始创建Ribbon客户端的项目,本节我们会讲到Ribbon调用的原理和自定义规则。

1.新建客户端

新建模块fw-cloud-ribbon,注意是New Module ,我们创建的是一个聚合工程
8bf966c86807a642dabead8e379b5d0e_MD5.webp

1.1 maven 配置

这里我们使用Spring Cloud 官方提供的spring-cloud-starter-netflix-ribbon

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>
</dependencies>

1.2 新建启动类


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

1.3 项目配置

server:
  port: 8772
spring:
  application:
    name: fw-feign
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

因为服务端是接口返回的User 类,所以接收端也需要同样的User 类

1.4 新建User 类

注意@NoArgsConstructor如果不添加反序列化的时候回报错


@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

    
    private long id;

    
    private String username;

    
    private String realname;

    
    private String email;

    
    private String remark;
}

1.5 添加 Ribbon 的配置


@Configuration
public class EurekaRibbonConfig {

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

1.5.1 @LoadBalanced 原理

在 RestTemplate 上加了一个 @LoadBalanced 注解就可以负载均衡。这是因为 Spring Cloud 做了大量的底层封装,做了很多简化。
内部的主要逻辑就是给 RestTemplate 增加拦截器,在请求之前对请求的地址进行了替换,或者根据具体的负载策略选择服务地址,然后去调用,这就是 @LoadBalanced 的原理。
Spring Web 为 HttpClient 提供了 Request 拦载器 ClientHttpRequestInterceptor,位于 spring-web jar 包下。
在 spring-cloud-commons 包中提供了负载均衡自动配置类 LoadBalancerAutoConfiguration ,里面维护了一个 @LoadBalanced 注解的 RestTemplate 列表,里面的静态类 LoadBalancerInterceptorConfig 注册了 负载均衡拦截器 LoadBalancerInterceptor,RestTemplateCustomizer 来添加拦截器列表。
负载均衡拦截器 LoadBalancerInterceptor 实现了 ClientHttpRequestInterceptor,主要逻辑在 intercept() 方法中,执行交给了 LoadBalancerClient,通过 LoadBalancerRequestFactory 来构建一个 LoadBalancerRequest 对象,createRequest 方法中通过 ServiceRequestWrapper 来执行替换 URI 的逻辑,核心是通过 reconstructURI() 方法实现,该方法的业务实现是在 RibbonLoadBalancerClient 类中 。

1.6 创建Ribbon Service


@Service
@Slf4j
public class EurekaRibbonService {

    @Autowired
    RestTemplate restTemplate;

    public User findUserById(Long id) {
        
        return restTemplate.getForObject("http://fw-cloud-ribbon-server/user/" + id, User.class);
    }
}

restTemplate.getForObject("http://fw-cloud-feign-server/user/" + id, User.class)这里需要添加正确的服务名称,RestTemplate 这样请求的好处就是不需要关心应用的IP,通过Eureka 注册中心发现是此服务名的服务提供者就会去请求。非常方便应用的水平拓展。

1.7 Ribbon RESTFUL 请求层


@RestController
public class EurekaRibbonController {
    @Resource
    private EurekaRibbonService eurekaRibbonService;

    
    @GetMapping("/user/{id:\\d+}")
    public User findUserById(@PathVariable long id){
        return eurekaRibbonService.findUserById(id);
    }
}

\\d+是一个正则表达式,这个正则表达式的含义就是请求的id只能是正整数

1.8 启动应用

通过postman 验证发现,分别输入localhost:8771/user/1
结果如下
a555108e84476a57c8bbcf25d8187342_MD5.png

b8329679c75c11ed8d7bd952f4dae2df_MD5.png

通过返回结果可以看到8773和8774一直在变化,轮询着变化,原因是什么呢?下面我们说一下负载均衡的策略。

1.9 负载均衡策略

Ribbon 默认的负载策略是轮询,同时也提供了很多其他的策略能够让用户根据业务需求来选择。负载均衡策略的根接口是 com.netflix.loadbalancer.IRule
216290fc6f7133a56d31f197fbc6c870_MD5.png

  • BestAvailableRule
    选择最小并发请求的服务器,每个客户端都会获得一个随机的服务器列表,如果服务器被标记为错误,则跳过。
  • AvailabilityFilteringRule
    用于过滤连接一直失败或读取失败而被标记为 circuit breaker tripped 状态的服务;或持有超过可配置限制的活动连接(默认值为 Integer.MAX_VALUE),即过滤掉高并发的后端服务。实际就是检查获取到的服务列表里,各个 Server 的 Status 。
  • ZoneAvoidanceRule
    根据区域和可用性来过滤服务器。使用 ZoneAvoidancePredicate 来判断 Zone 的使用是否达到阀值,过滤出最差 Zone 中的所有服务器; AvailabilityPredicate 用于过滤出并发连接过多的服务。
  • RandomRule
    随机分配流量,即随机选择一个 Server。
  • RetryRule
    向现有负载均衡策略添加重试机制。
  • ResponseTimeWeightedRule
    该策略已过期,同见 WeightedResponseTimeRule。
  • WeightedResponseTimeRule
    根据响应时间为每个服务器动态分配权重(Weight)分,然后台加权循环的方式使用该策略。响应时间越长,权重越低,被选中可能性越低。

以上是Ribbon 自带的负载策略,如果我们想改变策略怎么改?我们可以自定义策略吗? 请见下一节 Ribbon 自定义负载规则

2. 总结

本节主要讲解了Ribbon 如何负载的请求服务端的接口,并且讲解了LoadBalanced 原理和负载均衡策略,学习完本节,相信对Ribbon的操作已经了然于心了。