Spring Cloud LoadBalancer负载均衡

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、源码逻辑概述

  1. 拦截请求之后,进入负载均衡器LoadBalancerClient执行负载逻辑
  2. Spring Cloud LoadBalancer的默认实现为BlockingLoadBalancerClient,执行第一个execute方法,核心在于choose()方法,先选择负载均衡器。loadBalancerClientFactory.getInstance(serviceId);这个方法点进去看会发现是根据ReactorServiceInstanceLoadBalancer.class这个type去从Spring容器中取的Bean。默认是注入的它的实现类RoundRibbon轮询,即RoundRobinLoadBalancer.class这个类
  3. 选出负载均衡器之后,自然就开始执行负载均衡器的choose逻辑
  4. 逻辑中需要借助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也是不错的选择。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

(空白格)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值