1. 程式人生 > >Ribbon實現負載均衡

Ribbon實現負載均衡

Spring Cloud Ribbon是基於Netflix Ribbon實現的一套【客戶端】負載均衡工具。

Ribbon會自動幫助你基於某種規則(簡單輪詢、隨機連線等),也可以實現自定義的負載均衡演算法。 

負載均衡介紹:

    簡單來說負載均衡就是將使用者的請求ping平攤的分配到多個任務上,從而是系統達到HA(高可用)。

目前主要分兩種負載均衡:
  1. 集中式LB:偏硬體,服務的消費方和提供方之間使用獨立的LB設施(可以是硬體,如F5。也可以是軟體,如Nginx),由該設施負責把訪問請求以某種策略轉發至服務的提供方。
  2. 程序內LB:偏軟體, 將LB邏輯整合到消費方,消費方從服務註冊中心指導哪些地址可用,再自己選擇一個合適的伺服器(如Ribbon)。



 總結:Ribbon其實就是一個軟負載均衡的客戶端元件,可以和其他需要請求的客戶端結合使用。

Ribbon初步配置:

分析圖如下

實現前提:構建服務提供者叢集,新開兩個provider模組,註冊入Eureka,提供服務,叢集中服務名稱必須一致。

Ribbon是客戶端負載均衡工具!所以應該配置在客戶端。此處使用consume工程為例,配置僅需要在客戶端配置即可。配置步驟如下:

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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

2.對實現類加入@LoadBalanced註解,即以之前實現的RestTemplate為例。

在consume工程中的SpringBoot配置類ConfigBean中增加如下註解:

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/*  @Configuration 註解相當於代表該類是 applicationContext.xml */
@Configuration
public class ConfigBean
{ 
	@Bean
	@LoadBalanced
	public RestTemplate getRestTemplate(){
		return new RestTemplate();
	}
	
}
3. 在application.yml檔案中配置向註冊中心註冊,如果是作為消費者模組不提供服務,不應該註冊自己;
server:
  port: 9001

eureka:
  client:
    register-with-eureka: false #作為消費者不提供服務,不應該註冊自己
    service-url: 
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

4. 主啟動類中加入@EnableEurekaClient註解;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@EnableEurekaClient
public class DeptConsumer_9001_App {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_9001_App.class, args);
    }
}

5. 以上步驟1~4完成後即可在controller中直接通過服務名訪問系統中的微服務,服務名作為URI

consumer應用中controller實現如下:

    /* 原服務url */
    //private static final String REST_URL_PREFIX = "http://localhost:8001";
    /* 以服務名作為URI */
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUDDEMO-PROVIDER-DEPT";

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/dept/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/provider/dept/list", List.class);
    }

完成以上5個步驟就已經實現客戶端負載均衡功能(此處使用的是預設輪詢的規則進行負載均衡),可以訪問客戶端進行測試(略過)。

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

其中IRule是一個介面,有七個自帶的落地實現類,可以實現不同的負載均衡演算法規則:

RoundRobinRule                                                                                                       
輪詢規則(預設方法)

RandomRule

隨機

AvailabilityFilteringRule

先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,還有併發的連線數量超過閾值的服務,然後對剩餘的服務進行輪詢

WeightedResponseTimeRule

根據平均響應時間計算服務的權重。統計資訊不足時會按照輪詢,統計資訊足夠會按照響應的時間選擇服務
RetryRule
正常時按照輪詢選擇服務,若過程中有服務出現故障,在輪詢一定次數後依然故障,則會跳過故障的服務繼續輪詢。
BestAvailableRule
先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務

ZoneAvoidanceRule

預設規則,符合判斷server所在的區域的效能和server的可用性選擇服務

如何切換Ribbon中賦值均衡的規則,而不是使用預設的輪詢方式?

只需在客戶端(consume)工程中的SpringBoot配置類ConfigBean配置一個返回具體方法的bean即可,如下:

@Bean
	public IRule MyRule(){
		/* RandomRule為Ribbon中自帶規則實現之一,隨機規則 */
		return new RandomRule();
	}

自定義Ribbon負載均衡演算法

    如果覺得Ribbon中自帶的負載均衡規則無法滿足你的需求,那也可以自定義Ribbon負載均衡演算法。

需要注意的地方;

    自定義的Ribbon演算法類不能放在主啟動類所在的包及子包下(確切來說是不能放在@ComponentScan註解的包及子包下),否則會被全域性應用到Ribbon服務中。 應該把自定義演算法類放在另外新建的包下,且這個類應該是為【配置類】即加上註解@Configuration

實現自定義Ribbon負載均衡演算法的步驟:

1。將原來consumer工程中的配置類ConfigBean中返回 IRule 的Bean刪掉,然後新建一個包,定義配置類,返回自定義的負載均衡規則的實現類:

package com.wangcw.ribbon;

import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MySelfRule
{
	@Bean
	public IRule myRule() {
		/* 此處返回自定義的負載演算法規則 */
		return new RandomRule_Myself();
	}
}

2.實現自定義規則實現類RandomRule_Myself(此處實現每臺伺服器呼叫5次後,再繼續輪詢下一臺伺服器)

package com.wangcw.ribbon;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;

public class RandomRule_Myself extends AbstractLoadBalancerRule {

     /*
     total = 0 // 當total==5以後,我們指標才能往下走,
     index = 0 // 當前對外提供服務的伺服器地址,
     total需要重新置為零,但是已經達到過一個5次,我們的index = 1 */

    /* 總共被呼叫的次數,目前要求每臺被呼叫5次 */
    private int total = 0;
    /* 當前提供服務的機器號 */
    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) {
                return null;
            }

            if (total < 5) {
                server = upList.get(currentIndex);
                total++;
            } else {
                total = 0;
                currentIndex++;
                if (currentIndex >= upList.size()) {
                    currentIndex = 0;
                }
            }


            if (server == null) {
                Thread.yield();
                continue;
            }

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

            server = null;
            Thread.yield();
        }

        return server;

    }

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

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {}

}

3.在啟動類上加上註解 @RibbonClient(name = "微服務名",configuration = XXX.class) 註解指定需要用到負載均衡的微服務名及自定義演算法的class物件。

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@EnableEurekaClient
@RibbonClient(name="SPRINGCLOUDDEMO-PROVIDER-DEPT",configuration=MySelfRule.class)
public class DeptConsumer_9001_App {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_9001_App.class, args);
    }
}

4.啟動測試就可以發現已經生效。