1. 程式人生 > 程式設計 >學習SpringCloud Ribbon帶你從0到1

學習SpringCloud Ribbon帶你從0到1

一、Ribbon在微服務中的作用

1.什麼是Ribbon

​ 1.Ribbon是一個基於HTTP和TCP的客戶端負載均衡工具,它是基於Netflix Ribbon實現的

​ 2.它不像Spring Cloud服務註冊中心、配置中心、API閘道器那樣獨立部署,但是它幾乎存在於每一個Spring Cloud微服務中。包括Feign提供的宣告式服務呼叫也是基於Ribbon實現的。

​ 3.Ribbon提供了多種負載均衡演演算法,例如:輪詢、隨機等等。甚至包含自定義的負載均衡演演算法。

2.Ribbon解決了什麼問題

它解決並提供了微服務的負載均衡問題

二、集中式與程式內負載均衡的區別

1.負載均衡解決方案的分類

​ 目前業界主流的負載均衡方案可分為兩類:

  • 集中式負載均衡:即在consumer和provider中間使用獨立的負載均衡設施(可以是硬體,如F5,也可以是軟體,如:Nginx),由該設施把訪問請求通過某種策略轉發到provider

  • 程式內負載均衡:將負載均衡的邏輯整合到consumer,consumer從服務註冊中心獲知有哪些地址可用,然後自己再從這些地址中選擇一個合適的provider

    Ribbon屬於後者,它只是一個類庫,整合在consumer中,consumer通過它來獲得合適的provider地址。

2.兩種負載均衡方式架構圖

三、Ribbon入門案例

Ribbon對於叢集的服務採用的負載均衡的策略預設是輪詢

1.Consumer

@Service
public class UserService {

    @Autowired
    private LoadBalancerClient loadBalancerClient;  //ribbon:負載均衡器

    public List<User> getUsers(){

        //選擇呼叫的服務的名稱
            //ServiceInstance:封裝了服務的基本資訊,如:ip、埠號
        ServiceInstance si = loadBalancerClient.choose("eureka-provider"
); //拼接訪問服務的url StringBuffer sb = new StringBuffer(); //http://localhost:9090/user sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user"); System.out.println(sb.toString()); //SpringMVC RestTemplate RestTemplate restTemplate = new RestTemplate(); ParameterizedTypeReference<List<User>> type = new ParameterizedTypeReference<List<User>>() { }; //ResponseEntity:封裝了返回值資訊 ResponseEntity<List<User>> entity = restTemplate.exchange(sb.toString(),HttpMethod.GET,null,type); return entity.getBody(); } } 複製程式碼

2.Consumer的配置檔案

spring.application.name=eureka-consumer
server.port=9091

#設定服務註冊中心地址,向所有註冊中心做註冊
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
複製程式碼

3.Provider的叢集部署

3.1 將Provider打包,部署到linux環境中
3.2 建立啟動指令碼
#!/bin/bash
 
cd `dirname $0`
 
CUR_SHELL_DIR=`pwd`
CUR_SHELL_NAME=`basename ${BASH_SOURCE}`
 
JAR_NAME="springcloud-eureka-provider-0.0.1-SNAPSHOT.jar"
JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME
 
#JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m"
JAVA_MEM_OPTS=""
 
#SPRING_PROFILES_ACTIV="-Dspring.profiles.active=eureka2"
SPRING_PROFILES_ACTIV=""
LOG_DIR=$CUR_SHELL_DIR/logs
LOG_PATH=$LOG_DIR/${JAR_NAME%..log
 
echo_help()
{
    echo -e "syntax: sh $CUR_SHELL_NAME start|stop"
}
 
if [ -z $1 ];then
    echo_help
    exit 1
fi
 
if [ ! -d "$LOG_DIR" ];then
    mkdir "$LOG_DIR"
fi
 
if [ ! -f "$LOG_PATH" ];then
    touch "$LOG_DIR"
fi
 
if [ "$1" == "start" ];then
 
    # check server
    PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`
    if [ -n "$PIDS" ]; then
        echo -e "ERROR: The $JAR_NAME already started and the PID is ${PIDS}."
        exit 1
    fi
 
    echo "Starting the $JAR_NAME..."
 
    # start
    nohup java $JAVA_MEM_OPTS -jar $SPRING_PROFILES_ACTIV $JAR_PATH >> $LOG_PATH 2>&1 &
 
    COUNT=0
    while [ $COUNT -lt 1 ]; do
        sleep 1
        COUNT=`ps  --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l`
        if [ $COUNT -gt 0 ]; then
            break
        fi
    done
    PIDS=`ps  --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'`
    echo "${JAR_NAME} Started and the PID is ${PIDS}."
    echo "You can check the log file in ${LOG_PATH} for details."
 
elif [ "$1" == "stop" ];then
 
    PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`
    if [ -z "$PIDS" ]; then
        echo "ERROR:The $JAR_NAME does not started!"
        exit 1
    fi
 
    echo -e "Stopping the $JAR_NAME..."
 
    for PID in $PIDS; do
        kill $PID > /dev/null 2>&1
    done
 
    COUNT=0
    while [ $COUNT -lt 1 ]; do
        sleep 1
        COUNT=1
        for PID in $PIDS ; do
            PID_EXIST=`ps --no-heading -p $PID`
            if [ -n "$PID_EXIST" ]; then
                COUNT=0
                break
            fi
        done
    done
 
    echo -e "${JAR_NAME} Stopped and the PID is ${PIDS}."
else
    echo_help
    exit 1
fi
複製程式碼

4.啟動consumer

四、Ribbon常見的負載均衡策略

1.輪詢策略(預設):

​ 使用RoundRobinRule類,輪詢策略表示每次都順序取下一個Provider,比如有5個Provider,第一次取第一個,第二次取第二個,第三次取第三個,以此類推。

2.權重輪詢策略:

​ 使用WeightedResponseTimeRule類,根據每個Provider的響應時間分配一個權重,響應時間越長權重越小,被選中的可能性就越低。

​ 原理:一開始為輪詢策略,並開啟一個計時器,每30s收集一次每個provider的平均響應時間,當資訊足夠時給每個provider新增一個權重,並按權重隨機選擇,權重越高被選中的機率也就越高。

3.隨機策略:

​ 使用RandomRule類,從provider列表中隨機選擇一個provider

4.最少併發數策略:

​ 使用BestAvailableRule,選擇正在請求中併發數量小的provider,除非這個provider在熔斷中。

5.在選定的“負載均衡策略”基礎上進行重試機制

​ 使用RetryRule類, “選定的負載均衡策略”這個策略是輪詢策略 RoundRobinRule

​ 該重試策略先設定一個閾值時間段 ,如果在這個時間段內選擇provider不成功,則一直嘗試採用“選定的負載均衡策略:輪詢策略”最後選擇一個可用的provider

6.可用性敏感策略

​ 使用AvailabilityFilteringRule類,過濾效能差的provider一共有兩種:

​ 第一種:過濾掉Eureka中一直連線失敗的provider

​ 第二種:過濾掉高併發的provider

7.區域敏感性策略

​ 使用ZoneAvoidanceRule類,

​ 以一個區域為單位考察可用性,對於不可用的區域整個丟棄,從剩下區域中選可用的 provider

​ 如果這個 ip 區域內有一個或多 個例項不可達或響應變慢,都會降低該 ip 區域內其他 ip 被選中的權重

五、Ribbon指定其他負載均衡策略

1.修改程式碼更換負載均衡策略

1.1 建立專案

springcloud-eureka-consumer-LB

1.2 在啟動類中新增建立負載均衡策略物件的方法
/**
 * Author: LuYi
 * Date: 2019/11/5 17:32
 * Description: 描述
 */
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApplication {

    @Bean
    public RandomRule createRule() {
        return new RandomRule();
    }

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}
複製程式碼

2. 修改配置檔案更換負載均衡策略

#設定負載均衡策略 eureka-provider 為呼叫的服務的名稱
eureka-provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
複製程式碼

六、Ribbon點對點直連

1.建立專案

springcloud-eureka-consumer-direct

2.去掉Eureka的座標,新增Ribbon座標

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.luyi</groupId>
    <artifactId>springcloud-eureka-consumer-direct</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-eureka-consumer-direct</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <!--ribbon座標-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
    </dependencies>

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

</project>
複製程式碼

3.修改配置檔案去掉與Eureka相關的配置,新增新配置項

spring.application.name=eureka-consumer-direct
server.port=9091

#禁用 eureka
ribbon.eureka.enabled=false
#指定具體的服務例項清單
eureka-provider.ribbon.listOfServers=192.168.234.132:9090
複製程式碼

4.修改啟動類,去掉報錯程式碼

@SpringBootApplication
public class ConsumerApplication {

//    @Bean
//    public RandomRule createRule() {
//        return new RandomRule();
//    }

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}
複製程式碼