SpringCloud負載均衡


負載均衡

負載均衡建立在現有網絡結構之上,它提供了一種廉價有效透明的方法擴展網絡設備服務器的帶寬、增加吞吐量、加強網絡數據處理能力、提高網絡的靈活性和可用性。

負載均衡(Load Balance)其意思就是分攤到多個操作單元上進行執行,例如Web服務器FTP服務器企業關鍵應用服務器和其它關鍵任務服務器等,從而共同完成工作任務。

然而在SpringBoot中,負載均衡的概念被微小化,即同時有多個微服務提供者給一個消費者提高同樣的操作,我們需要在選擇誰提高服務來實現大流量情況下,避免服務器宕機的可能。

環境搭建

創建一個服務消費者兩個服務提供者:並注冊到服務中心。具體搭建過程參考如下:

https://www.cnblogs.com/Rampant/p/14775726.html

在配置環境是需要注意,如果項目中需要使用ribbon,在spring2.4.X 已經中spring-cloud-starter-netflix-eureka-client已經把ribbon剔除了,想要使用ribbon請自行導入包

搭建完成后如下所示:

訪問服務注冊中心如下:http://localhost:7001/

編寫業務代碼

1、首先,我們要實現負載均衡,必需將服務提供者集群。修改application.yml的服務名一致即可,修改如下

# 修改服務名一樣即可對於集群的服務
spring:
  application:
    name: cloud-provider-Load-Balance

2、編寫服務提供者業務,編寫一個即可,另外一個復制即可

package com.wyx.cloud.controller;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class ProviderController {

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/getInfo")
    public String getPort(){
        String msg = "獲取信息成功,提供服務的服務端口為:"+serverPort;
        return msg;
    }
}

3、編寫服務消費者業務,采用 RestTemplate

package com.wyx.cloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class ConsumerController {

    @Resource
    RestTemplate restTemplate;

    private final String PROVIDER_URL = "http://CLOUD-PROVIDER-LOAD-BALANCE";

    @GetMapping("/consumer/getInfo")
    public String getInfo(){

        return restTemplate.getForObject(PROVIDER_URL+"/getInfo",String.class);

    }
}

4、配置默認負載均衡

package com.wyx.cloud.config;

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
public class ApplicationConfig {

    @Bean
    @LoadBalanced  // 選擇不同的服務提供者,不然會因為不知道走哪一台服務提供者而報錯
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

5、運行測試:http://localhost/consumer/getInfo 多次點擊,發現默認的負載均衡就是 輪詢

LoadBalance

LoadBalance 是 Spring Cloud 提供了自己的客戶端負載均衡器抽象和實現。對於負載均衡機制,ReactiveLoadBalancer添加了接口,並為其提供了輪詢隨機的實現。為了讓實例從反應中選擇ServiceInstanceListSupplier 。目前,我們支持基於服務發現的實現,ServiceInstanceListSupplier

老版本中集成了Ribbon的默認的負載均衡(輪詢),也是和上面的一樣配置,他提供了很多負載均衡算法

1、隨機策略——RandomRule

2、輪詢策略——RoundRobinRule
注:Ribbon默認策略

3、重試策略——RetryRule

4、最低並發策略——BestAvailableRule

5、可用過濾策略——AvailabilityFilteringRule
過濾掉那些因為一直連接失敗的被標記為circuit tripped的后端server,並過濾掉那些高並發的的后端server(active connections 超過配置的閾值)

性能僅次於最低並發策略。

6、響應時間加權策略——WeightedResponseTimeRule
每隔30秒計算一次服務器響應時間,以響應時間作為權重,響應時間越短的服務器被選中的概率越大。

7、區域權衡策略——ZoneAvoidanceRule

Ribbon的負載均衡策略使用建議
一般情況下,推薦使用最低並發策略,這個性能比默認的輪詢策略高很多。

在上面的示例中,默認就是使用了負載均衡的輪詢方式,當然我們也可以切換為隨機的方式,只需要修改如下即可

1、創建一個配置類,並將負載均衡隨機類注入容器

package com.wyx.cloud.config;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@Configuration
public class CustomLoadBalancerClientConfiguration {
    // 官方寫好的隨機類
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
    }
}
// 和ribbon不一樣,這里必須配置在SpringBoot能掃描的包,及其子包下

老版本替換負載均衡

// 編寫一個類注入負載均衡算法類,這里很新版本不一樣,該類必須不能讓springboot掃描到,不能放在同級,或者子包下面
@Configuration
public class RibbonConfiguration {
  @Bean
  public IRule ribbonRule() {
    // 負載均衡規則,改為隨機,可以new的類,參考上面提供的類名,官方寫好的的類,也可以自定義負載均衡類
    return new RandomRule();
  }
}

老版本下,將使用該注解將其注入到負載均衡即可

/**
 * name 的 值代表需要負載均衡的服務名,切記服務名不能使用下划線。
 * configuration 代表我們添加的輪詢配置類,這里使用的是我們上一步配置的類。
 */
@RibbonClient(name = "user", configuration = RibbonConfiguration.class)

測試即可

2、使用注解替換我們要對那個服務的調用進行輪詢

package com.wyx.cloud.config;


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

/**
 在類上添加該注解,可以是主啟動類,也可以是配置類,都可以
 @LoadBalancerClient(value = "CLOUD-PROVIDER-LOAD-BALANCE", configuration = CustomLoadBalancerClientConfiguration.class)
 // value 的 值代表需要負載均衡的服務名,切記服務名不能使用下划線。
 // configuration 代表我們添加的輪詢配置類,這里使用的是我們上一步配置的類。
*/
@Configuration
@LoadBalancerClient(value = "CLOUD-PROVIDER-LOAD-BALANCE", configuration = CustomLoadBalancerClientConfiguration.class)
public class ApplicationConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

當然除了官方提供的配置類,我們也可以通過自定義的接口實現自己的負載均衡,我們自需要寫一個負載均衡的配置類即可。下面我們就來寫一個。

實現自定義負載均衡,我們實現官方給的ReactorServiceInstanceLoadBalancer接口即可

package com.wyx.cloud.config;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Random;

// 自定義一個類實現官方提供的負載均衡的算法
public class MyLoadBalance implements ReactorServiceInstanceLoadBalancer {

    // 服務列表
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private int count = 0;

    // serviceInstanceListSupplierProvider 是官方讀取讀取服務中心服務提供者的實例,並用列表返回。
    // 我們自需要將它使用構造器方法注入,便可獲取讀取的實例
    // 構造器
    public MyLoadBalance(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }


    // 重寫方法 即可實現自己需要的負載均衡負載均衡
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable();
        return supplier.get().next().map(this::getInstanceResponse);
    }


    private Response<ServiceInstance> getInstanceResponse(
            List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            return new EmptyResponse();
        }

        // 隨機獲取一個實例,並返回
        int size = instances.size();
        Random random = new Random();
        ServiceInstance instance = instances.get(random.nextInt(size));
        return new DefaultResponse(instance);
    }
}

2、將自己編寫的類注入容器

package com.wyx.cloud.config;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CustomLoadBalancerClientConfiguration {

    @Bean
    public ReactorServiceInstanceLoadBalancer MyLoadBalance(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
        return new MyLoadBalance(serviceInstanceListSupplierProvider);
    }
}

3、使用注解,將負載均衡設置為自己注入的bean

// 使用方法和上面一樣,不在重述
@LoadBalancerClient(value = "CLOUD-PROVIDER-LOAD-BALANCE", configuration = CustomLoadBalancerClientConfiguration.class)

OpenFeign

OpenFeign為微服務架構下服務之間的調用提供了解決方案,OpenFeign是一種聲明式、模板化的HTTP客戶端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP請求訪問遠程服務,就像調用本地方法一樣的,開發者完全感知不到這是在調用遠程方法,更感知不到在訪問HTTP請求。

簡單理解,openfeign就是利用聲明式,模板化的Http客戶端。

聲明式:即將服務提供者中提供的方法利用接口將其聲明,有什么方法

模板化:采用定義的接口經行調用。

使用流程

1、首先將我們搭建的環境還原

2、編寫服務提供者,和上面一樣具體參考上面即可

3、服務消費者,修改服務消費者的pom.xml文件

<!--添加 openfeign 的啟動依賴-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

4、主程序上添加注解自動開啟openfeign服務

@EnableFeignClients

5、將服務提供者的提供的服務抽象成接口

package com.wyx.cloud.service;


import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;

@Service  // 注入容器
@FeignClient("CLOUD-PROVIDER-LOAD-BALANCE") // 需要提供服務的服務注冊名,也是服務提供者的spring.application.name 
public interface ProviderInterface {
	
    // 提供的接口方法,注入容器即可
    @Bean
    @GetMapping("/getInfo")
    public String getPort();

}

6、服務調用,采用接口調用即可,如下

package com.wyx.cloud.controller;

import com.wyx.cloud.service.ProviderInterface;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class ConsumerController {

    @Resource
    ProviderInterface providerInterface;

    @GetMapping("/consumer/getInfo")
    public String getInfo(){
        return providerInterface.getPort();
    }
}

更多使用,可以參考官網:https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#spring-cloud-feign

超時控制

​ 超時控制,指feign客戶端遠程調用服務提供者,默認情況下,feign客戶端只等待一秒(集成ribbon的老版本,新版本不使用Ribbon的版本一直等待),如果因為網絡或者服務器宕機的原因,造成服務不可以,就返回錯誤信息,但是大多數情況下,1秒的響應時間過於短,我們需要自定義一下配置。(相對於老版本),

但是對於新版本,一直無限等待也會加重網絡擁塞,我們也需要設置一定的值,

只需要在application.yaml下添加如下配置即可

# 集成ribbon的老版本,使用這個配置
#設置feign 客戶端超時時間(openFeign默認支持ribbon)
ribbon:
#指的是建立連接所用的時間,適用於網絡狀況正常的情況下,兩端連接所用的時間
  ReadTimeout: 5000
#指的是建立連接后從服務器讀取到可用資源所用的時間
  ConnectTimeout: 5000


# 新版本修改以下默認配置
feign:
  client:
    config:
      default:
        // 連接超時的時間
        connectTimeout: 1000
        // 從發送信息到接收響應的時間
        readTimeout: 1000

更多配置參考官網的自定義配置

日志打印

Feign提供了日志打印功能,我們可以通過配置來調整日志級別,從而了解Feign中Http請求的細節。

1、配置日志打印功能,注入日志打印的依賴

package com.wyx.cloud.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    /*
    NONE:默認的,不顯示任何日志。
	BASIC:僅記錄請求方法、URL、響應狀態碼及執行時間。
	HEADERS:除了BASIC中定義的信息之外,還有請求和響應的頭信息。
	FULL:除了HEADERS中定義的信息之外,還有請求和響應的正文及元數據。
    */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

2、添加 application.yml

logging:
  level:
    com.wyx.cloud.service: debug

測試即可


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM