LoadBalancer负载均衡服务调用

chenxin
7
2024-11-21

Ribbon

已经进入维护阶段,不再使用

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。
SpringCloud提供了springcloud loadbalancer来替代Ribbon

SpringCloud loadbalancer概述

官网

是什么

Load Balancer负载均衡简单来说,即将请求平均的分配到多个服务上,从而实现系统的高可用,常见的负载均衡软件有nginx,lvs硬件F5等。
Spring Cloud LoadBalancer是由SpringCloud官方提供的一个开源的、简单易用的客户端负载均衡器,它包含在SpringCloud-commons中用它来替换了以前的Ribbon组件。相比较于Ribbon,SpringCloud LoadBalancer不仅能够支持RestTemplate,还支持WebClient(WeClient是Spring Web Flux中提供的功能,可以实现响应式异步请求)

客户端负载均衡VS服务端负载均衡

  • 服务端负载均衡:所有的请求发送到服务端后,由服务端实现转发请求,如nginx。
  • 客户端负载均衡: 调用微服务接口时,先在注册中心获取注册信息服务列表并缓存到JVM本地,在本地实现RPC远程服务调用,如loadbalancer

负载均衡的使用

SpringCloud LoadBalancer是一个抽象的接口,提供了许多实现。
在消费服务中使用的是RestTemplate作为LoadBalancer的客户端,使用了@LoadBalanced开启了负载均衡

启动提供服务

先将8001提供者服务复制一份并改为8002端口,那么此时的提供者服务就有两个实例,8001服务和8002服务。
分别启动两个服务后,在consul的控制台界面,可以看到,提供者服务有两个实例。

修改消费服务

Load Balancer是在客户端提供负载均衡,因此需要在消费服务引入Load Balancer

修改80服务
引入依赖

 <!--loadbalancer-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

修改Controller
添加以下内容

@GetMapping(value = "/consumer/pay/get/info")
    private String getInfoByConsul() {
        return restTemplate.getForObject(PaymentSrv_URL + "/pay/get/info", String.class);
    }

通过post访问,发现调用的提供服务的实例是轮询的,实现了负载均衡

负载均衡原理

官网解释
由官网代码可以看出,可以使用发现客户端获取指定服务的实例列表。
修改80消费服务Controller
加入下面内容

@Resource
    private DiscoveryClient discoveryClient;
    @GetMapping("/consumer/discover")
    public String discovery(){
        List<String> services = discoveryClient.getServices(); //获取服务列表
        for (String service : services){
            System.out.println(service);
        }
        System.out.println("======================");
        List<ServiceInstance> instances=discoveryClient.getInstances("cloud-payment-service"); //获取服务的实例列表
        for (ServiceInstance instance : instances){
            System.out.println(instance.getInstanceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
        }
        return instances.get(0).getServiceId()+":"+instances.get(0).getPort();
    }

使用postman发送请求后,结果如下

首先是consul的三个服务
之后是消费服务的两个实例
原理
负载均衡算法(默认是轮询算法):rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。
如: List [0] instances = 127.0.0.1:8002
   List [1] instances = 127.0.0.1:8001
当总请求数为1时: 1 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位2时: 2 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
当总请求数位3时: 3 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位4时: 4 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
8001和8002组成集群,那么根据负载均衡算法,每次请求都是不一样的机器

负载均衡算法


SpringCloud LoadBalancer默认使用轮询算法RoundRobinLoadBalancer,还提供了一种随机算法RandomLoadBalancer

默认的负载均衡算法

  • 轮询:public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer
  • 随机:public class RandomLoadBalancer implements ReactorServiceInstanceLoadBalancer

两种负载均衡算法均是实现了ReactorServiceInstanceLoadBalancer接口,而 ReactorServiceInstanceLoadBalancer则是继承了ReactorLoadBalancer接口

/*
 * Copyright 2012-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.loadbalancer.core;

import reactor.core.publisher.Mono;

import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;

/**
 * A Reactor based implementation of {@link ReactiveLoadBalancer}.
 *
 * @param <T> - type of the response
 * @author Spencer Gibb
 */
public interface ReactorLoadBalancer<T> extends ReactiveLoadBalancer<T> {

	/**
	 * Choose the next server based on the load balancing algorithm.
	 * @param request - an input request
	 * @return - mono of response
	 */
	@SuppressWarnings("rawtypes")
	Mono<Response<T>> choose(Request request);

	default Mono<Response<T>> choose() {
		return choose(REQUEST);
	}

}

如果要实现自定义的负载均衡算法,实现ReactorServiceInstanceLoadBalancer接口即可
一般情况,轮询算法足够使用。

算法切换
将轮询算法切换为随机算法
修改RestTemplateConfig即可

@Configuration
@LoadBalancerClient(
        //下面的value值大小写一定要和consul里面的名字一样,必须一样,指定的微服务使用随机算法
        value = "cloud-payment-service",configuration = RestTemplateConfig.class)
public class RestTemplateConfig
{
    @Bean
    @LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);

        return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}
动物装饰