PS:放假偷偷摸摸卷Java
-------------------------------------------------正文分割线----------------------------------------------------
概念:负载均衡换句话说就是将请求并发访问转发给后台多台云服务器实例,实现应用程序的流量均衡,性能上实现业务水平扩展。负载均衡还通过故障自动切换及时地消除服务的单点故障,提升服务的可用性。
一、快速开始
1、添加依赖
负载均衡本身并没有服务发现的能力,因此需要借助注册中心nacos或eureka来完成它的功能
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<scope>provided</scope>
</dependency>
<!-- SpringCloud Ailibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2、bootstrap配置nacos服务发现
spring:
application:
name: demo-consumer
nacos:
discovery:
enabled: true
server-addr: ${NACOS_SERVER_ADDR:localhost:8848}
namespace: public
config:
enabled: false
fileExtension: yaml
server-addr: ${NACOS_SERVER_ADDR:localhost:30897}
3、打注解@LoadBalancer
@Bean
@LoadBalanced
public RestTemplate loadbalancerRestTemplate() {
return new RestTemplate();
}
到此就能实现负载均衡了,简单吧
底层逻辑
1、LoadBalancer核心:拦截器
负载均衡底层是通过一个拦截器来实现的,拦截RestTemplate发出的请求,对地址中的服务名进行了修改。
2、源码逻辑概述
- 拦截请求之后,进入负载均衡器LoadBalancerClient执行负载逻辑
- Spring Cloud LoadBalancer的默认实现为BlockingLoadBalancerClient,执行第一个execute方法,核心在于choose()方法,先选择负载均衡器。loadBalancerClientFactory.getInstance(serviceId);这个方法点进去看会发现是根据ReactorServiceInstanceLoadBalancer.class这个type去从Spring容器中取的Bean。默认是注入的它的实现类RoundRibbon轮询,即RoundRobinLoadBalancer.class这个类
- 选出负载均衡器之后,自然就开始执行负载均衡器的choose逻辑
- 逻辑中需要借助ServiceInstanceListSupplier提供获取实例的方法来帮助我们对实例进行筛选
3、源码详细
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
// for backwards compatibility
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
// 1、拦截请求
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
// uri:统一资源定位符(协议+ip+端口);
// url:统一资源标识符(协议+ip+端口+资源path)
final URI originalUri = request.getURI();
// 服务名
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
}
负载均衡器LoadBalancer的实现类BlockingLoadBalancerClient中持有LoadBalancerClientFactory,它提供了许多获取客户端信息的能力,例如:getInstance获取负载均衡器实例信息
public class BlockingLoadBalancerClient implements LoadBalancerClient {
//...省略
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
String hint = getHint(serviceId);
LoadBalancerRequestAdapter<T, TimedRequestContext> lbRequest = new LoadBalancerRequestAdapter<>(request,
buildRequestContext(request, hint));
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId);
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
// 2、选择负载均衡器实例,注意这里还没有开始筛选实例
ServiceInstance serviceInstance = choose(serviceId, lbRequest);
if (serviceInstance == null) {
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, new EmptyResponse())));
throw new IllegalStateException("No instances available for " + serviceId);
}
return execute(serviceId, serviceInstance, lbRequest);
}
//...省略
@Override
public <T> ServiceInstance choose(String serviceId, Request<T> request) {
// loadBalancer是拿到的负载均衡器也就是原生的LoadBalancerClient的两个实现RoundRobin或Random,也可以自己实现
ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
if (loadBalancer == null) {
return null;
}
Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();
if (loadBalancerResponse == null) {
return null;
}
return loadBalancerResponse.getServer();
}
// choose方法用于选择实例
@Override
public <T> ServiceInstance choose(String serviceId, Request<T> request) {
ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
if (loadBalancer == null) {
return null;
}
Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();
if (loadBalancerResponse == null) {
return null;
}
return loadBalancerResponse.getServer();
}
//...省略
}
4、接口和类的继承实现关系
5、如何自定义负载均衡策略
需求:假设我们需要通过bootstrap配置选择不同的负载策略
步骤一:DynamicLoadBalancer动态负载类
通过实现ReactorServiceInstanceLoadBalancer接口,重写choose方法,实现动态配置策略的需求
public class DynamicLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Logger log = LoggerFactory.getLogger(DynamicLoadBalancer.class);
private final DynamicLoadBalancerConfiguration dynamicLoadBalancerConfiguration;
private final Map<String, ReactorServiceInstanceLoadBalancer> cache = new LinkedCaseInsensitiveMap<>();
public DynamicLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory,
List<LoadbalancerClientFactory> loadbalancerClientFactories,
DynamicLoadBalancerConfiguration dynamicLoadBalancerConfiguration) {
for (LoadbalancerClientFactory loadbalancerClientFactory : loadbalancerClientFactories) {
cache.put(loadbalancerClientFactory.getClientName(),
loadbalancerClientFactory.createClient(environment, loadBalancerClientFactory));
}
this.dynamicLoadBalancerConfiguration = dynamicLoadBalancerConfiguration;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
String loadBalancerConfig = dynamicLoadBalancerConfiguration.getLoadBalancer();
if (!cache.containsKey(loadBalancerConfig)) {
log.warn("bootstrap config loadbalancer:{" + dynamicLoadBalancerConfiguration.getLoadBalancer() + "} error, use default balancer RoundRobin");
}
//找到负载均衡策略对象
ReactorServiceInstanceLoadBalancer loadBalancerClient =
cache.containsKey(loadBalancerConfig) ? cache.get(loadBalancerConfig) : cache.get(LoadBalancerStrategyConstants.DEFAULT_CLIENT);
//负载均衡策略根据本次请求信息,选择出一个实例
return loadBalancerClient.choose(request);
}
}
步骤二:负载均衡器动态工厂类
实现ReactorServiceInstanceLoadBalancer接口可以自定义负载均衡器
public class RandomLoadBalancerClientFactory implements LoadbalancerClientFactory{
@Override
public String getClientName() {
return LoadBalancerStrategyConstants.RANDOM;
}
@Override
public ReactorServiceInstanceLoadBalancer createClient(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
//获得应用服务名称
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// 关键在于此处,自定义的逻辑可以自己实现一个类
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
步骤三:动态负载均衡自动装配Bean类
自动装配负载均衡器关键在于@LoadBalancerClients注解,还记得前面提到的getInstance()方法中通过type获取Spring容器中注入的Bean,通过此配置可以装配我们注入的Bean,通过注入自定义类,走我们想要的逻辑。到此动态负载均衡大致实现了,具体细节不过多獒述。
@Configuration
@LoadBalancerClients(defaultConfiguration = {DynamicLoadBalancer.class})
@EnableConfigurationProperties({DynamicLoadBalancerConfiguration.class})
public class LoadBalancerAutoConfiguration {
@Bean
public RoundRobinLoadBalancerClientFactory roundRobinLoadBalancerClientFactory() {
return new RoundRobinLoadBalancerClientFactory();
}
@Bean
public RandomLoadBalancerClientFactory randomLoadBalancerClientFactory() {
return new RandomLoadBalancerClientFactory();
}
@Bean
public CustomLoadBalancerClientFactory customLoadBalancerClientFactory() {
return new CustomLoadBalancerClientFactory();
}
}
最后:
可能是因为Ribbon迭代的版本比Spring Cloud LoadBalancer更多,Spring Cloud LoadBalancer原生的负载均衡策略较少,只有轮询和随机;Ribbon除此之外还支持最小并发、权重等等。因此想要Spring Cloud LoadBalancer支持更多策略需要自己定义,如果公司不要求使用高版本的Spring,使用Ribbon也是不错的选择。