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
测试即可