Ribbon之負載均衡

服務端的負載均衡(Nginx)

 我們用戶服務發送請求首先打到Ng上,然後Ng根據負載均衡算法進行選擇一個服務調用,而我們的Ng部署在服務器上的,所以Ng又稱爲服務端的負載均衡

客戶端負載均衡(ribbon)

spring cloud ribbon是 基於NetFilix ribbon 實現的一套客戶端的負載均衡工具,Ribbon客戶端組件提供一系列的完善的配置,如超時,重試等。通過Load Balancer(LB)獲取到服務提供的所有機器實例,Ribbon 會自動基於某種規則(輪詢,隨機)去調用這些服務。

Ribbon也可以實現我們自己的負載均衡算法。

內置的負載均衡算法

ZoneAvoidanceRule  默認規則,複合判斷server所在區域的性能和server的可用性選擇服務器

RandomRule(隨機選擇一個Server)

RoundRobinRule 輪詢選擇, 輪詢index,選擇index對應位置的Server

RetryRule 先按照RoundRobinRule的策略獲取服務,如果獲取服務失敗則在指定時間內會進行重試,獲取可用的服務

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

BestAvailableRule  會先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務

WeightedResponseTimeRule 根據平均響應時間計算所有服務的權重,響應時間越快服務權重越大被選中的概率越高。

剛啓動時如果統計信息不足,則使用RoundRobinRule策略,等統計信息足夠, 會切換到WeightedResponseTimeRule

自定義的負載均衡算法(隨機)

寫一個TestRestTemplate類繼承RestTemplate,從寫doExucute()方法。

/**
 * 根據RestTemplate特性自己改造
 */
@Slf4j
public class TestRestTemplate extends RestTemplate {

    private DiscoveryClient discoveryClient;

    public TestRestTemplate (DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }

    protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
                              @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {

        Assert.notNull(url, "URI is required");
        Assert.notNull(method, "HttpMethod is required");
        ClientHttpResponse response = null;
        try {

            log.info("請求的url路徑爲:{}",url);
            //把服務名 替換成我們的IP
            url = replaceUrl(url);

            log.info("替換後的路徑:{}",url);

            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }
            response = request.execute();
            handleResponse(url, method, response);
            return (responseExtractor != null ? responseExtractor.extractData(response) : null);
        }
        catch (IOException ex) {
            String resource = url.toString();
            String query = url.getRawQuery();
            resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
            throw new ResourceAccessException("I/O error on " + method.name() +
                    " request for \"" + resource + "\": " + ex.getMessage(), ex);
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }


    /**
     * 方法實現說明:把微服務名稱  去註冊中心拉取對應IP進行調用
     * http://product-center/selectProductInfoById/1
     */
    private URI replaceUrl(URI url){

        //1:從URI中解析調用的調用的serviceName=product-center
        String serviceName = url.getHost();
        log.info("調用微服務的名稱:{}",serviceName);

        //2:解析我們的請求路徑 reqPath= /selectProductInfoById/1
        String reqPath = url.getPath();
        log.info("請求path:{}",reqPath);


        //通過微服務的名稱去nacos服務端獲取 對應的實例列表
        List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
        if(serviceInstanceList.isEmpty()) {
            throw new RuntimeException("沒有可用的微服務實例列表:"+serviceName);
        }

        String serviceIp = chooseTargetIp(serviceInstanceList);

        String source = serviceIp+reqPath;
        try {
            return new URI(source);
        } catch (URISyntaxException e) {
            log.error("根據source:{}構建URI異常",source);
        }
        return url;
    }

    /**
     * 方法實現說明:從服務列表中 隨機選舉一個ip
     */
    private String chooseTargetIp(List<ServiceInstance> serviceInstanceList) {
        //採取隨機的獲取一個
        Random random = new Random();
        Integer randomIndex = random.nextInt(serviceInstanceList.size());
        String serviceIp = serviceInstanceList.get(randomIndex).getUri().toString();
        log.info("隨機選舉的服務IP:{}",serviceIp);
        return serviceIp;
    }
}

個人博客:https://www.upheart.top/ 

個人公衆號,日常分享一個知識點,每天進步一點點,面試不慌:

 

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