1. 程式人生 > >Spring Cloud(四)Ribbon負載均衡

Spring Cloud(四)Ribbon負載均衡

1 概述

1.1 Ribbon簡介

Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡工具。Ribbon會自動幫助你基於某種規則(簡單輪詢、隨機連線等),也可以實現自定義的負載均衡演算法。

1.2 負載均衡

  • 英文名稱:Load Balance,微服務或分散式叢集中常用的一種應用。
  • 簡單來說負載均衡就是將使用者的請求ping平攤的分配到多個任務上,從而是系統達到HA(高可用)。
  • 兩種負載均衡:
    • 集中式LB:偏硬體,服務的消費方和提供方之間使用獨立的LB設施(可以是硬體F5,也可以是軟體Nginx),由該設施負責把訪問請求以某種策略轉發至服務的提供方。
    • 程序內LB:偏軟體, 將LB邏輯整合到消費方,消費方從服務註冊中心指導哪些地址可用,再自己選擇一個合適的伺服器。Ribbon就是一個程序內LB,它只是個類庫,集成於消費方程序,消費方通過它獲取服務提供方的地址。

1.3 架構圖

在這裡插入圖片描述

2 Ribbon配置

Ribbon是客戶端負載均衡工具!!!所以應該配置在客戶端

2.1 新建consumer ribbon工程

  1. 加入依賴,因為Riboon需要依賴Eureka執行,所以要同時加入Eureka依賴。
<dependency>
	<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency>
  1. 對訪問模版的實現類加入@LoadBalanced註解。
@Bean
@LoadBalanced
public
RestTemplate getRestTemplate() { return new RestTemplate(); } }
  1. 在application.yml檔案中配置向註冊中心註冊,如果是作為消費者模組不提供服務,不應該註冊自己。
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
    register-with-eureka: false             #作為消費者不提供服務,不應該註冊自己
  1. 主啟動類中加入@EnableEurekaClient註解。
@SpringBootApplication
@EnableEurekaClient
public class SpringCloudConsumerRibbonApplication80 {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudConsumerRibbonApplication80.class, args);
    }
}
  1. 以上步驟1~4完成後即可在controller中直接通過服務名訪問系統中的微服務,服務名作為URI。
private static final String REST_URL_PREFIX= "http://spring-cloud-provider/";

2.2 構建provider叢集

  1. 構建叢集,新開兩個provider模組,將原provider的程式碼部分和pom.xml中依賴照搬到新的provider中。
  2. 將原provider中application.yml檔案照搬到新provider,並修改埠號,若新的provider使用自己的資料庫,則修改資料庫資訊(其他配置也一樣,如修改別名instance-id)。
  3. 叢集中服務名稱必須一致!!!不要修改。
spring:
  application:
    name: spring-cloud-provider   #同一叢集下必須使用同一服務名!!!!!
  1. 依次啟動3個uereka、三個provider、consumer ribbon服務,測試:

在這裡插入圖片描述 在這裡插入圖片描述 在這裡插入圖片描述 可以看到,三個peovider交替提供服務,證明了Ribbon預設使用輪訓負載均衡演算法

3 Ribbon核心元件IRule

IRule:根據特定演算法從服務列表中選取一個需要訪問的服務

3.1 七大方法

IRule是一個介面,七大方法是其自帶的落地實現類。

  1. RoundRobinRule:輪詢(預設方法)
  2. RandomRule:隨機
  3. AvailabilityFilteringRule:先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,還有併發的連線數量超過閾值的服務,然後對剩餘的服務進行輪詢
  4. WeightedResponseTimeRule:根據平均響應時間計算服務的權重。統計資訊不足時會按照輪詢,統計資訊足夠會按照響應的時間選擇服務
  5. RetryRule:正常時按照輪詢選擇服務,若過程中有服務出現故障,在輪詢一定次數後依然故障,則會跳過故障的服務繼續輪詢。
  6. BestAvailableRule:先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務
  7. ZoneAvoidanceRule:預設規則,符合判斷server所在的區域的效能和server的可用性選擇服務

3.2 切換規則方法

只需在配置類中配置一個返回具體方法的bean即可

@Bean
public IRule myRule(){
	return new RandomRule();    
}

3.3 自定義Ribbon負載均衡演算法

3.3.1 自定義Ribbon演算法

自定義的Ribbon演算法類不能放在主啟動類所在的包及子報下(確切來說是不能放在@ComponentScan註解的包及子包下),否則會被全域性應用到Ribbon服務中。應該把自定義演算法類放在另外新建的包下,且這個類應該是為配置類。也可以將該類排除掉(其實與普通切換負載均衡規則類似,只不過是位置不同而已,普通的可以放在主啟動類所在的包,自定義的要放在外面的包下)。

  1. 定義排除註解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcludeAnno {
}
  1. 自定義規則類。
/**
 * @author: 陳玉林
 * @modifiedBy:
 * @date: Create In 10:52 2018/10/19
 * @description: 每個服務被輪訓五次
 */
public class  MyRibbonRule  extends AbstractLoadBalancerRule {
    // total = 0 // 當total==5以後,我們指標才能往下走,
    // index = 0 // 當前對外提供服務的伺服器地址,
    // total需要重新置為零,但是已經達到過一個5次,我們的index = 1
    // 分析:我們5次,但是微服務只有8001 8002 8003 三臺,OK?


    private int total = 0; 			// 總共被呼叫的次數,目前要求每臺被呼叫5次
    private int currentIndex = 0;	// 當前提供服務的機器號

    public Server choose(ILoadBalancer lb, Object key)
    {
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();

            int serverCount = allList.size();
            if (serverCount == 0) {
				/*
				 * No servers. End regardless of pass, because subsequent passes only get more
				 * restrictive.
				 */
                return null;
            }

//			int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
//			server = upList.get(index);


//			private int total = 0; 			// 總共被呼叫的次數,目前要求每臺被呼叫5次
//			private int currentIndex = 0;	// 當前提供服務的機器號
            if(total < 5)
            {
                server = upList.get(currentIndex);
                total++;
            }else {
                total = 0;
                currentIndex++;
                if(currentIndex >= upList.size())
                {
                    currentIndex = 0;
                }
            }


            if (server == null) {
				/*
				 * The only time this should happen is if the server list were somehow trimmed.
				 * This is a transient condition. Retry after yielding.
				 */
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }

            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }

        return server;

    }

    @Override
    public Server choose(Object key)
    {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig)
    {
        // TODO Auto-generated method stub

    }
}
  1. 將自定義規則加入spring容器。
/**
 * @author: 陳玉林
 * @modifiedBy:
 * @date: Create In 10:43 2018/10/19
 * @description: 該類必須放置在掃描不到的包下, 或者新增排除, 雖然他必須有註解
 */
@Configuration
@ExcludeAnno
public class MyRibbonConfig {
    @Bean
    public IRule ribbonRule() {
        return new MyRibbonRule();
    }

}
  1. 主啟動類新增@RibbonClient(name = “微服務名”,configuration = XXX.class)註解指定需要用到負載均衡的微服務名及自定義演算法的class物件。然後將該配置類從啟動掃描類排除掉。
// 使用此規則不可放在 可掃描的路徑下, 如果非要放置, 需要加自定義註解
@RibbonClient(name = "spring-cloud-provider", configuration = MyRibbonConfig.class)
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {ExcludeAnno.class})})
public class SpringCloudConsumerRibbonApplication80 {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudConsumerRibbonApplication80.class, args);
    }
}
  1. 去掉切換的規則。
  2. 依次啟動3個uereka、三個provider、consumer ribbon服務,測試,成功。