Spring Cloud 之 ribbon

參考:<<重新定義springcloud>>

  各個視頻

什麼是ribbon?它解決了什麼問題

  • Ribbon是一個基於HTTP和TCP的客戶端負載均衡工具,他是基於Netflix Ribbon實現的。
  • 它不像spring cloud服務註冊中心,配置中心,API網關那樣獨立部署,但是它幾乎在每一個spring cloud微服務中,包括feign提供的聲明式服務調用也是基於Ribbon實現的。
  • ribbon提供了很多種負載均衡算法,例如 輪詢、隨機 等等。甚至是包含自定義的負載均衡算法。
  • 他解決了微服務的負載均衡的問題

負載均衡解決方案的分類

目前業界主流的負載均衡方案課分爲倆類:

1、集中式負載均衡,就是在consumer和provider之間使用獨立的負載均衡設施(可以是硬件,如F5,也可以是軟件 ,nginx)由這些設施 負責把訪問請求 通過 某種策略轉發至provider

2、進程內負載均衡,將負載均衡邏輯集成到consumer,consumer從註冊中心獲取有哪些服務地址,從中通過某種策略挑選出一個provider

Ribbon 就是屬於後者,他只是一個類庫,集成於consumer進程,consumer通過他來獲取到provider的地址

他們的區別架構圖

 Ribbon的負載均衡算法策略

id 命名 策略類 描述
1 輪詢算法 RoundRibbonRule 按順序循環選擇server
2 隨機算法 randomRule 隨機選擇server
3 重試策略 retryRule 在一個配置時間內當選擇的server不成功,則一直嘗試選擇一個可用的server
4 最低併發策略 BestAvailableRule 逐個考擦server,如果server斷路器打開,則忽略,選擇請求中併發最小的server,
 5  可用過濾策略  availabilityFilteringRule  過濾掉一直連接失敗並被標記爲ciruruit tripped的server,過濾掉哪些高併發連接的server(active connections超過配置的閾值)
 響應時間加權策略  responseTimeWeightRule  根據server的響應時間分配權重,響應時間越長,權重越低,響應時間越短,權重越高,被選中的概率就越高,這個策略很貼切,綜合了。網絡 磁盤 IO等因素
 7  區域權衡策略  zoneavoidanceRule  

 綜合判斷server所在區域的性能和server的可用性輪詢選擇server,判斷一個區域是否可用,如果不可用直接將這個區域的sever都丟棄

如:多機房情況

使用RIbbon實現的入門實例:

1.先創建一個父工程:只有pom文件 hello-springcloud

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xiaodao</groupId>
    <artifactId>hello-springcloud</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>eureka_server</module>
        <module>client-a</module>
        <module>client-a</module>
        <module>ribbon-client</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
    </parent>
    <!--  利用傳遞依賴,公共部分  -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- springboot web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <!-- 管理依賴 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
View Code

2.創建一個eurekaserver服務:client-a

pom文件:

<?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>hello-springcloud</artifactId>
        <groupId>com.xiaodao</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
View Code

bootstrap.yml 和啓動類

server:
  port: 8888
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/


/**
* Created by xiaodao
* date: 2019/7/17
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

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

3.創建一個provider 

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

啓動類:

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

controller:

package com.xiaodao.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/add")
    public String add(Integer a, Integer b, HttpServletRequest request){
        return " From Port: "+ request.getServerPort() + ", Result: " + (a + b);
    }
}
View Code

3.創建第3個項目:ribbon-client

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

bootstrap.yml

spring:
  application:
    name: ribbon-loadbalancer
server:
  port: 7777
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8888/eureka/
#默認是hostname註冊,改成ip註冊
instance: prefer-ip-address: true

啓動類:

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonLoadbalancerApplication {

    public static void main(String[] args) {
        SpringApplication.run(RibbonLoadbalancerApplication.class, args);
    }
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

testController:

@RestController
public class TestController {
    
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/add")
    public String add(Integer a, Integer b) {
        String result = restTemplate
                .getForObject("http://CLIENT-A/add?a=" + a + "&b=" + b, String.class);
        System.out.println(result);
        return result;
    }
}

現在我們的項目都創建完成了.我們啓動倆個client-a不過端口不一樣.啓動ribbon-client 啓動 hello-springcloud

 

當我們請求localhost:7777/add?a=1&b=10 的時候我們可以看到打印的日誌:

 From Port: 7070, Result: 11
 From Port: 7071, Result: 11
 From Port: 7070, Result: 11
 From Port: 7071, Result: 11
 From Port: 7070, Result: 11
 From Port: 7071, Result: 11
 From Port: 7070, Result: 11

換一箇中策略來實現

前面提到過那麼多策略模式.我們模式的是輪詢模式,現在來挑選一個最簡單的可以看出來的隨機

我們有配置文件和java倆種配置

   @Bean
    public IRule ribbonRule() {
        return new RandomRule();//這裏配置策略,和配置文件對應
    }

 配置文件方式:

spring:
  application:
    name: ribbon-loadbalancer
server:
  port: 7777
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8888/eureka/

  instance:
    prefer-ip-address: true
CLIENT-A:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

點對點直連,讓ribbon調試更加方便快捷

什麼是點對點直連?

cusumer和provider點對點直連不經過註冊中心,爲什麼呢?因爲開發環境都是有eureka來做註冊中心,開發中大家的idea都是啓動的,你要和對方進行聯調,很難聯調

 

如果我和張三聯調.負載均衡經常發給李四和王五,這種情況沒法聯調.有人說我要和張三聯調,讓李四和王五先停下來.這怎麼行?

如何實現呢?

1.在consumer就是你要調用對方的自己的服務:

將uereka移除

  <!--  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>-->
@SpringBootApplication
//@EnableDiscoveryClient
public class RibbonLoadbalancerApplication {

    public static void main(String[] args) {
        SpringApplication.run(RibbonLoadbalancerApplication.class, args);
    }
    
  @Bean
//    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    /*@Bean
    public IRule ribbonRule() {
        return new RandomRule();//這裏配置策略,和配置文件對應
    }*/
}

pom文件:

spring:
  application:
    name: ribbon-loadbalancer
server:
  port: 8082
#eureka:
#  client:
#    serviceUrl:
#      defaultZone: http://localhost:8888/eureka/
#
#  instance:
#    prefer-ip-address: true
#禁用eureka
ribbon:
  eureka:
    enabled: false
CLIENT-A:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    listOfServers: http://127.0.0.1:7070,http://127.0.0.1:7071

在調用的時候需要這麼寫:

@RestController
public class TestController {
    
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private LoadBalancerClient loadBalancerClient;//ribbon 負載均衡客戶端

    @GetMapping("/add")
    public String add(Integer a, Integer b) {
        ServiceInstance si=loadBalancerClient.choose("CLIENT-A");
        StringBuffer sb=new StringBuffer("");
        sb.append("http://");
        sb.append(si.getHost());
        sb.append(":");
        sb.append(si.getPort());
        sb.append("/add?a="+a+"&b="+b);
        System.out.println(sb.toString());
        String result = restTemplate.getForObject(sb.toString(), String.class);
//        String result = restTemplate
//                .getForObject("http://CLIENT-A/add?a=" + a + "&b=" + b, String.class);
        System.out.println(result);
        return result;
    }
}

這個時候,就可以實現點對點的聯調.

關於@loadBanced的實現.

就是基於實際是通過實現 ClientHttpRequestInterceptor 實現的

LoadBalancerInterceptor
LoadBalancerClient
RibbonLoadBalancerClient

然後經過LoadBalancerAutoConfiguration自動配置將loadbalanceInterceptor變爲resttemplate的的攔截器

@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {

   @Bean
   public LoadBalancerInterceptor ribbonInterceptor(
         LoadBalancerClient loadBalancerClient,
         LoadBalancerRequestFactory requestFactory) {
      return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
   }

   @Bean
   @ConditionalOnMissingBean
   public RestTemplateCustomizer restTemplateCustomizer(
         final LoadBalancerInterceptor loadBalancerInterceptor) {
      return restTemplate -> {
         List<ClientHttpRequestInterceptor> list = new ArrayList<>(
               restTemplate.getInterceptors());
         list.add(loadBalancerInterceptor);
         restTemplate.setInterceptors(list);
      };
   }

}

這裏不做多講.畢竟在下也是隻知皮毛.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章