一、负载均衡概述
上一篇文章中,我们在集成OpenFeign的过程中提示我们需要加入了一个依赖就是: spring-cloud-starter-loadbalancer。 顾名思义,这个包的作用就是用来做负载均衡的。
简单解释一下什么是负载均衡,就是当我们的服务有多个实例的时候,比如我们userservice, 一个端口号是8081,一个端口号是8082。当然这是因为我只有一天电脑只能一台机器上模拟。正常情况我们的两个用户服务肯定应该是部署在两台机器上,这样就可以防止其中一台机器出现故障而导致的服务不可用的问题,这叫做分布式部署。而部署了两个相同的服务,当我们有大量的请求过来的时候,就可以把请求平均分布到两台机器上分别处理,降低每个服务的负载,使他们的压力得到均衡。这就是服务均衡。
那我们实际来验证一下,我们加入了这个包是否实现了负载均衡的功能。怎么验证的,就是同时启动两个用户服务。然后调用的时候,看请求是否能够均衡的打到两个服务上。
二、负载均衡验证
我们先来改造一下我们的用户接口,由于两个服务的端口号是不一样的,我们就在程序当中打印一下端口号。
package com.lsqingfeng.springcloud.user.controller;
import com.lsqingfeng.springcloud.common.base.Result;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @className: UserController
* @description:
* @author: sh.Liu
* @date: 2022-03-29 17:25
*/
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Value("${server.port}")
private String port;
@GetMapping("hello")
public Result hello(String name){
log.info("被请求到的服务端口号为: " + port);
return Result.success(name);
}
}
然后按照上篇文章的方式,启动两个用户服务。
另外一个:
然后自启动订单服务,订单服务的接口中已经通过openFeign成功调用了userservice中的接口。我们观察日志,看看调用的是哪个服务。
第一次调用:
服务一打印了日志:
再次调用:
服务二打印了日志。这就验证了请求是在两个机器上分别出现了。说明我们的负载均衡已经实现了。
而且Spring-Cloud-Loadbalancer默认的负载均衡策略就是轮训策略。也就是一人一下,轮着来。而负载均衡也有其他的策略。
三、spring-cloud-starter-loadbalancer
Spring Cloud LoadBalancer是一个客户端负载均衡器,类似于Ribbon,但是由于Ribbon已经进入维护模式,并且Ribbon 2并不与Ribbon 1相互兼容,所以Spring Cloud全家桶在Spring Cloud Commons项目中,添加了Spring cloud Loadbalancer作为新的负载均衡器,并且做了向前兼容。由于我们使用的SpringCloud版本比较新,引入的nacos依赖中已经不包含ribbon的相关依赖了。如果你使用的是相对比较老的版本,在使用Spring-cloud-loadbalancer的时候,需要将 ribbon的相关依赖排除掉,同时在配置文件中禁用ribbon。
关于spring-cloud-loadbalancer的具体内容,可以参看一下官方文档:
地址: Cloud Native Applications
通过文档中的描述,
Spring Cloud 中内部微服务调用默认是 http 请求,主要通过下面三种 API:
- RestTemplate:同步 http API
- WebClient:异步响应式 http API
- 三方客户端封装,例如 openfeign
如果项目中加入了 spring-cloud-loadbalancer 的依赖并且配置启用了,那么会自动在相关的 Bean 中加入负载均衡器的特性。
- 对于 RestTemplate,会自动对所有
@LoadBalanced
注解修饰的 RestTemplate Bean 增加 Interceptor 从而加上了负载均衡器的特性。 - 对于 WebClient,会自动创建
ReactorLoadBalancerExchangeFilterFunction
,我们可以通过加入ReactorLoadBalancerExchangeFilterFunction
会加入负载均衡器的特性。 - 对于三方客户端,一般不需要我们额外配置什么。
所以由于我们使用的是openFein,如果想要自己定义一套负载均衡的算法策略,我找了很多的资料都没有相关的介绍,应该是目前来讲,可能还不支持针对openFeign的定制化负载均衡策略。如果大家有相关资料也可以分享给我。目前来讲,这种轮训机制的策略也可以满足一般的负载均衡需求。
当然如果确实有自定义负载均衡策略的需要,那么我们也可以把openFeign的调用改为RestTemplate或者 WebClient。这样就可以实现了。
代码已经上传至gitCode:一缕82年的清风 / spring-cloud-learning · GitCode, 有需要的朋友可以下载下来查看。
四、负载均衡实现
上面的内容写于2022年,现在是2023年,我又重新开始写这个专题的时候,又重新研究了一下这个问题。发现OpenFeign和 loadbalancer集成的时候,可以可以来配置负载均衡策略的。 只不过负载均衡的策略目前只有两种,一种是随机、一种是轮询。这里来讲解一下具体的配置过程。
首先要注意,我们要在调用方进行配置!在调用方进行配置!在调用方进行配置!在调用方进行配置!
那我们进入调用方的工程,先添加一个负载均衡的配置类。 创建一个conf包,添加一个类如下:
package com.lsqingfeng.springcloud.conf;
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;
/**
* @className: LoadBalancedConfig
* @description:
* @author liushuai
* @date 2023/3/8 3:45 PM
*/
@Configuration
public class LoadBalancedConfig {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// 随机方式,轮询方式: RoundRobinLoadBalancer
return new RandomLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
这里最后返回的RandomLoadBalancer 就是代表的随机轮询策略机制。
然后在feignClient上添加一个注解。
@LoadBalancerClient(name="userservice", configuration = LoadBalancedConfig.class)
这里的configuration就指向我们刚刚写的那个配置类。完整代码如下:
package com.lsqingfeng.springcloud.order.client;
import com.lsqingfeng.springcloud.common.base.Result;
import com.lsqingfeng.springcloud.conf.LoadBalancedConfig;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @interface: UserFeignClient
* @description:
* @author: sh.Liu
* @date: 2022-03-29 19:10
*/
@Component
@FeignClient(name="userservice")
@LoadBalancerClient(name="userservice", configuration = LoadBalancedConfig.class)
public interface UserFeignClient {
/**
* userController中的hello
* @param name
* @return
*/
@GetMapping("/user/hello")
Result hello(@RequestParam("name") String name);
}
接下来就可以了。我们启动三个userService实例。 一个OrderService实例。 通过调用接口,查看被调用的服务情况。(关于idea启动过个服务报错,前面讲过)
我们调用了三次order/getOrder接口。查看UserService服务中的的日志
发现8084被调用了两次,8082被调用了一次。 看样子应该是随机的。
接下来换成轮询的试试。修改OrderService中的配置类。改为:RoundRobinLoadBalancer
@Configuration
public class LoadBalancedConfig {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// 随机方式,轮询方式: RoundRobinLoadBalancer
return new RoundRobinLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
重新启动OrderService.
再来调用三次服务:
发现每个服务都被调用了一次,验证了我们的猜测。说明我们还是可以对负载均衡的策略进行配置的。
如果觉得这两种方式满足不了,那么我们就只能自己去写实现了。可以参考RoundRobinLoadBalancer的方式尝试着去做下。其实如果没有啥特殊需求,这两种策略其实也足够使用了。
好了,关于这一部分的内容,我们就先到这里吧。代码已经上传到了gitCode。