The introduction

Hello, this is Anyin.

We learned about the basic Spring Cloud Loadbalancer workflow in the previous article, and today we will implement a client Loadbalancer based on the same network segment priority.

Demand analysis

Before implementing a custom client load balancer based on same network segment priority, let’s talk about why this requirement is needed.

In our daily microservice application development, if a system has five microservice applications, it is normal to deploy five microservice applications in an online development environment. When A developer iterates on A microservice A, the service needs to be started locally, which results in two instances of microservice A in the registry (Nacos as an example), one in the online development environment and one in the developer’s home. The diagram below:

What’s the problem with this?

Problem a

When another microservice B relies on microservice A, when it calls microservice A, it gets two instances of microservice A from the registry: one online development environment and one developer local. Because the default client load balancing mode is round training, microservice B will be called from these two instances in round training. When the round training is transferred to the local instance of the developer, the call will fail due to network access problems.

Question 2

When another developer develops microservice C, it relies on microservice A and needs to coordinate with the previous developer. At this time, because microservice A has two instances and the default load balancing mode is rotation training, microservice C will be called to the instance of microservice A in the online development environment from time to time, which brings great inconvenience to the development.

Code implementation

In the premise of code implementation, there is another condition that the network segment of the online development environment network is different from that of the office environment network. For example, the online development environment is K8S cluster deployment, and the IP segment of POD starts with 10, while the network segment of the office environment generally starts with 192.

Based on this premise, in order to solve the above two problems, we implement a client load balancer based on the same network segment priority, that is, when the network segment of the caller is consistent with the network segment of the called, the client load balancer is preferred.

The extension point that the Spring Cloud Loadbalancer component opens up to us is the LoadBalancerClients annotation, through which we can inject our own custom configuration through the defaultConfiguration property.

So, here we add a LoadBalanceAutoConfiguration class, then through LoadBalancerClients annotation configuration custom configuration, the code is as follows:

@Configuration(proxyBeanMethods = false)
@LoadBalancerClients(defaultConfiguration = LoadBalanceConfig.class)
@AutoConfigureBefore({ ReactorLoadBalancerClientAutoConfiguration.class, LoadBalancerBeanPostProcessorAutoConfiguration.class })
public class LoadBalanceAutoConfiguration {}Copy the code

LoadBalanceConfig is our custom configuration classes, it is registered two Bean instance: ServiceInstanceListSupplier (service provider instance list) and ReactorLoadBalancer (load balancer), the code is as follows:

@Configuration(proxyBeanMethods = false)
public class LoadBalanceConfig {

    @Bean
    @ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "net-segment")
    public ServiceInstanceListSupplier serviceInstanceListSupplier(ConfigurableApplicationContext context) {
        ServiceInstanceListSupplier supplier = ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching().build(context);
        return new NetSegmentServiceInstanceListSupplier(supplier);
    }

    @Bean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, ServiceInstanceListSupplier serviceInstanceListSupplier) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RoundRobinLoadBalancer(newSimpleObjectProvider<>(serviceInstanceListSupplier), name); }}Copy the code
  • We added a configuration property:spring.cloud.loadbalancer.configurations, only the attribute isnet-segmentWill instantiate our custom service instance list provider.
  • We signed ourselves up manuallyReactorLoadBalancerIs not registered in the default way

Finally, let’s look at the implementation of a service instance list provider based on the same network segment preference

@Slf4j
public class NetSegmentServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier {

    private InetUtils inetUtils;

    public NetSegmentServiceInstanceListSupplier(ServiceInstanceListSupplier delegate) {
        super(delegate);
        inetUtils = new InetUtils(new InetUtilsProperties());
    }

    @Override
    public Flux<List<ServiceInstance>> get() {
        return getDelegate().get().map(this::filterByNetSegment);
    }

    private List<ServiceInstance> filterByNetSegment(List<ServiceInstance> instances) {
        InetAddress host = inetUtils.findFirstNonLoopbackAddress();
        if(host == null) {return instances;
        }
        String resourceIp = host.getHostAddress();
        List<ServiceInstance> targetList = Lists.newArrayList();
        for(ServiceInstance instance : instances){
            if(IPV4Util.isSameAddress(resourceIp, instance.getHost())){ targetList.add(instance); }}if(CollectionUtil.isEmpty(targetList)){
            return instances;
        }
        returntargetList; }}Copy the code
  • InetUtilsThe local IP is obtained from an instance of this class, as is this classDiscoveryClientExample in obtaining the local IP address.
  • throughIPV4UtilTool class to check whether the local IP network segment is consistent with the target instance network segment. If yes, add the IP address to the list.
  • If no instance is found on the same network segment, the system returns the instance as it is to avoid errors.

The last

This achieves a client load balancing based on the same network segment priority. Did you learn?

Source code address: anyin-cloud