【源码专题】微服务Spring Cloud Ribbon模块架构源码解析

91 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情

简介

目前主流的负载均衡的方案分为两种:一种是集中式的负载均衡,在消费者和服务提供方中间使用独立的大力方式进行负载,另一种则是客户端自己做负载均衡,根据自己的请求情况做负载。

Ribbon的6大插件模块

  • ribbon-loadbalancer:负载均衡模块,可独立使用也可以和别的模块一起使用,Ribbon的内置负载均衡算法都实现在其中;
  • ribbon-eureka:基于Eureka封装的模块,能够快速的方便的集成Eureka;
  • ribbon-transport:就要Netty 实现多协议的支持,比如Http、TCP、Udp等;
  • ribbon-httpclient:基于Apache HttpClient 封装的Rest的客户端,集成了负载均衡模块,可以一直接在项目中使用来调用接口;
  • ribbon-okhttp:基于OkHttp 封装的Feign客户端,基于OkHttp的非阻塞异步调用,性能优秀;
  • ribbon-example: Ribbon的一些代码使用示例;
  • ribbon-core:一些比较和音的具有通用性的代码,客户端API的一些配置和其他API的定义。

1. 引入依赖

        <dependency>
            <groupid>com.netflix.ribbon</groupid>
            <artifactid>ribbon</artifactid>
            <version> 2.3.0</version>
        </dependency>
        <dependency>
            <groupid>com.netflix.ribbon</groupid>
            <artifactid>ribbon-loadbalancer</artifactid>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupid>com.netflix.ribbon</groupid>
            <artifactid>ribbon-core</artifactid>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupid>io.reactivex</groupid>
            <artifactid>rxjava</artifactid>
            <version>1.3.8</version>
        </dependency>

2. 编写客户端调用接口

public String testRibbon(){
    List serverList = new ArrayList<>();
    serverList.add(new Server("localhost",8081));
    serverList.add(new Server("localhost",8082));
    ILoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
    for (int i=0;ibuilder().withLoadBalancer(loadBalancer)
         .build().submit(new ServerOperation() {
             @Override
             public Observable call(Server server) {
                 try{
                     String addr = "http://"+server.getHost()+":"+server.getPort()+"/user/hello";
                     System.out.println("调用地址:"+addr);
                     URL url = new URL(addr);
                     HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                     httpURLConnection.setRequestMethod("GET");
                     httpURLConnection.connect();
                     InputStream inputStream = httpURLConnection.getInputStream();
                     byte[] data = new byte[inputStream.available()];
                     inputStream.read();
                     return Observable.just(new String(data));
                 }catch (Exception e){
                     return Observable.error(e);
                 }
             }
         }).toBlocking().first();
         System.out.println("调用结果:"+result);
         }
         return "finish";
         }

运行后的结果如下:

调用地址:http://localhost:8082/user/hello 调用结果:hello everyone

调用地址:http://localhost:8081/user/hello 调用结果:hello everyone

调用地址:http://localhost:8082/user/hello 调用结果:hello everyone

调用地址:http://localhost:8081/user/hello 调用结果:hello everyone

调用地址:http://localhost:8082/user/hello 调用结果:hello everyone

根据上面的输入可以看出负载起了作用。

<dependency>
    <groupid>org.springframework.cloud</groupid>
    <artifactid>spring-cloud-starter-netflix-ribbon</artifactid>
    <version> 2.2.2.RELEASE</version>
</dependency>

2. 在RestTemplate的配置上加LoadBalance注解

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

3. 接口的调用

@RestController
public class ArticleController {
​
    private final RestTemplate restTemplate;
​
    @Autowired
    public ArticleController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
​
    @GetMapping("/article/callhello2")
    public String callHello2(){
        return restTemplate.getForObject("http://eureka-client-user-service/user/hello",String.class);
    }
​
}

@LoadBalanced作用:给RestTemplate增加拦截器,在请求之前对请求的地址进行替换,或者根据具体的负载均衡的策略选择服务地址,然后再去调用。

7大负载均衡算法

  1. BestAvailable:选择一个最小的并发请求的 Server ,逐个考察 Server ,如果Server标记为错误责跳过,然后再选择ActiveRequestCount中最小的 Server 。
  2. AvailabilityFilteringRule:过滤掉那些一直连接失败并且被标记为 circuit tripped的后端 Server ,并过滤掉那些高并发的后端 Server 或者使用一个 AvailabilityPredicate 来包含过滤 Availability 的窥觊,其实就是检查 Status 里记录的各个 Server 的运行状态。
  3. ZoneAvoiddanceRule:使用 ZoneAvoiddancePredicate 和AvailabilityPredicate 来判断是否选择某个Server ,前一个判断判定一个Zone的运行性能是否可用,删除不可用的Zone的所有Server,后一个用于过滤连接数过多的Server。
  4. RadomRule:随机选择一个Server。
  5. RoundRobinRule:轮询选择,轮询index,选择index对应位置的Server
  6. RetryRule:对选定的负载均衡策略机上重试机制,也就是说当选定了某个策略进行请求负载时在一个配置时间段内若选择Server不成功,责一直尝试使用 subRule的方式选择一个可用的Server.
  7. ResponseTimeWeightedRule(已弃用) :作用同 WeightedResponseTimeRule ,ResponseTimeWeightedRule后来改名为WeightedResponseTimeRule。
  8. WeightedResponseTimeRule:根绝相应时间分配一个Weight,相应时间越长 Weight越小,被选中的可能性越低。
public class MyRule implements IRule {
​
    private ILoadBalancer loadBalancer;
    @Override
    public Server choose(Object o) {
        //主要选择的逻辑都在这个方法里面
        List allServers = loadBalancer.getAllServers();
        for (Server server: allServers) {
            System.out.println(server.getHost());
        }
        return allServers.get(0);
    }
​
    @Override
    public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
      this.loadBalancer = loadBalancer;
    }
​
    @Override
    public ILoadBalancer getLoadBalancer() {
        return loadBalancer;
    }
}

欢迎关注白羊,感谢观看ヾ(◍°∇°◍)ノ゙