自定义负载均衡算法

下面笔者结合实际的编码实现一个自定义的负载均衡算法,并使用该负载均衡器来测试负载均衡的效果。

ltd.newbee.cloud 包下新建 balancer 包,并新建 NewBeeCloudLoadBalancer 类。注意,该类一定要实现 ReactorServiceInstanceLoadBalancer 接口,源码如下:

package ltd.newbee.cloud.balancer;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.client.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.client.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class NewBeeCloudLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider

    {

        private String serviceName;

    public
        NewBeeCloudLoadBalancer(ObjectProvider < ServiceInstanceListSupplier > serviceInstanceListSupplierProvider, String serviceName)
        {
            this.serviceName = serviceName;
            this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        }

        private AtomicInteger atomicCount = new AtomicInteger(0);
        private AtomicInteger atomicCurrentIndex = new AtomicInteger(0);

        @Override
        public Mono<Response<ServiceInstance>> choose (Request request){
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable();
        return supplier.get().next().map(this::getInstanceResponse);
    }

        /**
         * 使用自定义方法获取服务
         *
         * @param instances
         * @return
         */
        private Response<ServiceInstance> getInstanceResponse (
            List < ServiceInstance > instances) {
        ServiceInstance serviceInstance = null;

        if (instances.isEmpty()) {
            System.out.println("注册中心无可用的实例:" + serviceName);
            return new EmptyResponse();
        }

        //累加并得到值(请求次数)
        int requestNumber = atomicCount.incrementAndGet();

        //自定义算法
        if (requestNumber < 2) {
            serviceInstance = instances.get(atomicCurrentIndex.get());
        } else {
            // 已经大于了2了,重置
            atomicCount = new AtomicInteger(0);

            // atomicCurrentIndex 变量加1
            atomicCurrentIndex.incrementAndGet();

            if (atomicCurrentIndex.get() >= instances.size()) {
                atomicCurrentIndex = new AtomicInteger(0);
                serviceInstance = instances.get(instances.size() - 1);
                return new DefaultResponse(serviceInstance);
            }
            // 从可用的实例中获取一个实例来进行操作,类似轮询算法
            serviceInstance = instances.get(atomicCurrentIndex.get() - 1);
        }
        return new DefaultResponse(serviceInstance);
    }

        @Override
        public Mono<Response<ServiceInstance>> choose () {
        return ReactorServiceInstanceLoadBalancer.super.choose();
    }
}

自定义的负载均衡算法与轮询算法类似,不过并不是执行一次请求就使用下一个实例,而是每个实例执行两次才会轮询到下一个实例,该值由 atomicCount 变量控制。

自定义的负载均衡算法编写完成后,还需要做一次配置才能使用。在 config 包中新建配置类 NewBeeCloudLoadBalancerConfiguration,源码如下:

package ltd.newbee.cloud.config;

import ltd.newbee.cloud.balancer.NewBeeCloudLoadBalancer;
import org.springframework.cloud.client.ServiceInstance;
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.core.env.Environment;

public class NewBeeCloudLoadBalancerConfiguration {

    @Bean
    public ReactorLoadBalancer<ServiceInstance> customLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new NewBeeCloudLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

需要在启动类上添加一个 @LoadBalancerClient 注解,将该配置类和目标服务做好关联。启动类代码改动如下:

@SpringBootApplication
@EnableDiscoveryClient
//对 newbee-cloud-goods-service 服务使用自定义的负载均衡算法
@LoadBalancerClient(value = "newbee-cloud-goods-service", configuration = NewBeeCloudLoadBalancerConfiguration.class)
public class LoadBalancerApplication {

    public static void main(String[] args) {
        SpringApplication.run(LoadBalancerApplication.class, args);
    }
}

最后,重启项目并测试效果,自定义负载均衡算法编码完成。