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