前言
本文为SpringCloud的学习笔记,如有错误,希望各位高手能指出,主要介绍SpringCloudLoadBalancer的基本概念和实战
文章目录
什么是LoadBalancer
❀ LoadBalancer是一种流量分发机制,专业中文叫法“负载均衡”,这种流量分发可以通过软件或者硬件来实现服务端系统资源的均衡利用,提高整体系统的高可用和性能。
负载均衡分类
服务端负载均衡
顾名思义,服务端负载均衡就是在服务器层面实现流量分发,如下图所示:
客户端负载均衡
在知道服务器负载均衡后,客户端负载均衡也容易理解,这是在客户端就直接实现流量分发,也就是说,在客户端就知道请求去往何处,如下图所示:
客户端负载均衡有:Ribbon、SpringCloudLoadBalancer
服务端负载均衡和客户端负载均衡的优缺点
服务端负载均衡:
-
优点:
-
缺点:
客户端负载均衡:
-
优点:
-
缺点:
综合来看,服务端负载均衡更适用于大型系统和复杂架构,可以提供统一的处理策略和更好的可扩展性。而客户端负载均衡则更适用于轻量级系统和简单架构,可以提供更灵活和定制化的负载均衡策略。
常见负载均衡策略
- 轮询(Round Robin):轮询策略就是按照顺序把每个请求分发给服务端,依次循环。适用于后端服务器性能相近,且每个服务器处理时间相近的情况
- 随机选择(Random):把请求随机发给后端服务器,适用于后端服务器性能相近,且每个服务器处理时间相近的情况,但无法保证请求均与分发
- 最少链接(Least Connections):最少连接策略将请求分发给当前连接数最少的后端服务器,可以确保后端服务器连接均衡,需要维护连接计数器。需要注意的是,
“最少连接数”只是一个估计值
,在千变万化的网络环境下,连接数也在变化。 - IP 哈希(IP Hash):根据客户端IP地址进行哈希计算,根据哈希值将请求发送到对应服务器上。这种策略可以用于确保来自同一客户端的请求都会被发送到后端服务器,适用于会话保持的情况。
- 加权轮询(Weight Round Robin):给每个后端服务器分配一个权重值,然后按照权值比例来分发请求,这种策略可以用来处理服务器性能不均衡的情况。
- 加权随机(Weigh Random):与加权轮询类似,但是按照权重值来随机选择后端服务器。这也可以用来处理后端服务器性能不均衡的情况,但是分发更随机。
- 最短响应时间(Least Response Time):根据后端服务器的响应时间情况,分发请求到响应时间最短的服务器。这样可以确保客户端或者最快的响应,适用于低延迟的应用。
为什么要学习Spring Cloud LoadBalancer
作为早期版本中SpringCloud内置的负载均衡器Ribbon,在SpringCloud 2020.0.0中已经被移除,可以在 https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2020.0-Release-Notes中进行查看更新日志。
取而代之的是SpringCloud官方提供的Spring-Cloud-LoadBalancer负载均衡
但Spring Cloud LoadBalancer中仅提供了三种负载均衡策略:轮询、加权、自定义(Nacos的负载均衡算alibaba自定义)
可以通过Spring Cloud LoadBalancer的配置类LoadBalancerClientConfiguration查看底层配置
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {
private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;
public LoadBalancerClientConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty("loadbalancer.client.name");
return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
// 以下忽略......
}
这段代码中,reactorServiceInstanceLoadBalancer()方法的返回类型ReactorLoadBalancer< T >就是策略的实现接口,具体实现类如下图所示:
值得我们注意的是,在这个方法中,默认返回了RoundRobinLoadBalancer这个类,也就说SpringCloudLoadBalancer默认使用轮询策略。
如何使用?
在项目中添加Spring Cloud OpenFeign、注册中心(Nacos)、在添加Spring Cloud LoadBalancer则会在接口调用时直接使用SpringCloudLoadBalance。
项目搭建
我们先构建一个多模块项目SpringCloud-LoadBalancer-demo,具体过程省略,详情可以到这篇文章下查看《Nacos 注册中心介绍与实操》
在Provider子模块中,我们创建一个简单controller类
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private ServletWebServerApplicationContext servletWebServerApplicationContext;
@RequestMapping("/getid")
public String getId(@RequestParam Integer id){
return servletWebServerApplicationContext.getWebServer().getPort()+"-provider-"+id;
}
}
随后我们启动两个相同的Provider服务并注册到Nacos中
对于Consumer,我们需要建立sevice接口和controller类,controller类调用service接口的方法,该方法会在注册中心中通过负载均衡(默认轮询)获取服务端信息,然后将请求发送到指定服务端请求数据。
Sevice代码
@Service
@FeignClient("nacos-provider")
public interface UserService {
@RequestMapping("/user/getid")
String getId(@RequestParam("id") Integer id); //@RequestParam("id") 不能省略必须全部带上
}
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/getid")
public String getId(@RequestParam("id") Integer id){
return userService.getId(id);
}
}
@SpringBootApplication
@EnableFeignClients // 开启OpenFeign
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
那么接下来启动Consumer验证底层默认策略是否为轮询
会发现在我们没有配置负载均衡策略的时候,每次访问都是两个服务端轮流使用
第一次访问:
第二次访问:
使用随机策略
创建随机策略配置类(不需要@configuration注解)
public class RandomLoadBalancerConfig {
@Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty("loadbalancer.client.name");
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
细心的同学就已经发现了,这个方法跟上面提到的LoadBalancerClientConfiguration .reactorServiceInstanceLoadBalancer()方法实现很像。这是因为官方文档中就是这么告诉我们如果要使用其他策略就需要这样做,复制一下方法体,修改返回的策略对象。
官方文档链接:https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer
当然如果只是写一个配置类还不够,我们还需要到启动类(全局配置)或者服务类(局部配置)中去启用它
全局配置:
@SpringBootApplication
@EnableFeignClients // 开启OpenFeign
@LoadBalancerClients(defaultConfiguration = RandomLoadBalancerConfig.class)
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
这时再去访问http://localhost:8080/user/getid?id=1时就会发现,我们不断刷新,它访问的值就完全随机了。(不知道怎么做动态图,不方便演示T_T)
局部配置
@Service
@FeignClient("nacos-provider")
@LoadBalancerClient(value = "nacos-provider", configuration = RandomLoadBalancerConfig.class)
public interface UserService {
@RequestMapping("/user/getid")
String getId(@RequestParam("id") Integer id); //@RequestParam("id") 不能省略必须全部带上
}
使用Nacos的负载均衡策略策略
@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerConfig.class)
public class NacosLoadBalancerConfig {
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty("loadbalancer.client.name");
return new NacosLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name, nacosDiscoveryProperties);
}
}
与其他两个官方儿子不一样,Nacos的负载均衡器还需要额外数据信息
启动类添加注解
@SpringBootApplication
@EnableFeignClients // 开启OpenFeign
//@LoadBalancerClients(defaultConfiguration = RandomLoadBalancerConfig.class)
@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerConfig.class)
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
我们会发现Nacos中使用的不是轮询,翻阅底层实现,会发现他是权重策略,所以为了验证这个,我们到Nacos注册中心中去修改一下服务端权重
这个时候我们再去访问,就可以发现它会大部分请求都使用53922这个端口的服务(无动图不方便演示,读者可以自己实操体验一下)
局部配置NacosNacos策略是生效的,可以自行测试
自定义策略(IP Hash为例)
要实现自定义策略需要三个步骤
编写自定义策略类
回到我们下面这张图
不难发现,ReactorLoadBalancer< T >是我们负载均衡器的实现上层骨架,这个就是设计模式中的“模板方法模式”,所有人都必须按照骨架进行开发,那么我们自定义策略类也需要如此,但我们去实现的接口是他的子接口ReactorServiceInstanceLoadBalancer,重写它的两个choose方法。
MyLoadBalancer实现
public class MyLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Log log = LogFactory.getLog(MyLoadBalancer.class);
private final String serviceId;
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
public MyLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
}
public Mono<Response<ServiceInstance>> choose(Request request) {
// 提供备选服务列表
ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
// 选择服务实例
return supplier.get(request).next().map((serviceInstances) -> {
return this.processInstanceResponse(supplier, serviceInstances);
});
}
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
// 从备选列表中选择一个具体的服务实例
Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
// 实例为空
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + this.serviceId);
}
return new EmptyResponse();
} else { // 服务不为空
// 获取Request对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String ipAddress = request.getRemoteAddr();
System.out.println("用户IP"+ipAddress);
int hash = ipAddress.hashCode();
int index = hash%instances.size();
ServiceInstance instance = (ServiceInstance)instances.get(index);
return new DefaultResponse(instance);
}
}
}
代码看起来多,但是你如果看一下官方两个亲儿子的源码就会发现,其实只需要复制一下,修改getInstance这段核心代码就可以了
封装自定义负载均衡
public class MyLoadBalancerConfig {
@Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty("loadbalancer.client.name");
return new MyLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
全局配置自定义负载均衡配置类
启动类
@SpringBootApplication
@EnableFeignClients // 开启OpenFeign
//@LoadBalancerClients(defaultConfiguration = RandomLoadBalancerConfig.class)
//@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerConfig.class)
@LoadBalancerClients(defaultConfiguration = MyLoadBalancerConfig.class)
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
缓存
Spring Cloud LoadBalancer在获取实例时有两种选择
1.即使获取:每次从注册中心得到最新健康的实例,效果好,但是得一直询问,系统开销比较大
2.缓存服务列表:每次得到服务列表之后,缓存一段时间,这样能保证性能,同时也能兼容一定的及时性
Spring Cloud LoadBalancer中默认开始缓存服务列表
Spring Cloud LoadBalancer默认缓存重要特性有两项
我们可以通过以下配置改变这些配置
Spring:
cloud:
loadbalancer:
cache:
ttl:35s
capacity: 1024
# enable: false # 开启缓存
END
希望对你有帮助
原文地址:https://blog.csdn.net/weixin_59216829/article/details/134592899
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_1062.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!