1. Series of articles

  • You must understand and can understand microservices series 1: service registration
  • You must understand and can understand microservices series 2: service unregister
  • The third in the series of microservices that you must and can understand: service invocation

2. The preface

The service name, service IP, and port are registered with the registry during startup, as described in the must-know microservices series 1: Service Registration

In you must understand or can understand the micro service series three: service call a remote service call, in the call to the remote service, first according to the service name to obtain the target service address information list, and then according to the relevant load algorithm to calculate the target service address. This involves service discovery and load balancing, which is what this article will cover

3. Service discovery

In the micro service series 3: Service Invocation, which you must understand and can understand, you will be able to make remote calls through HTTP and return results. In fact, there will be service discovery operations between remote calls, which are used to obtain service machine information for load.

3.1 Service Selection

@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();
}
Copy the code

First create the load balancing through LoadBalancerClientFactory client, then client access to the target service through load balancing instance information

3.2 Load Balancing Client Declaration

@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
   String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
   return new RoundRobinLoadBalancer(
         loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
Copy the code

Can see need ServiceInstanceListSupplier when creating the load balance the client object, from naming can guess what this object is used to obtain information service instance list

3.3 ServiceInstanceListSupplier statement

@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default", matchIfMissing = true)
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context) {
   return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching().build(context);
}


public ServiceInstanceListSupplierBuilder withBlockingDiscoveryClient(a) {
   if(baseCreator ! =null && LOG.isWarnEnabled()) {
      LOG.warn("Overriding a previously set baseCreator with a blocking DiscoveryClient baseCreator.");
   }
   this.baseCreator = context -> {
      DiscoveryClient discoveryClient = context.getBean(DiscoveryClient.class);

      return new DiscoveryClientServiceInstanceListSupplier(discoveryClient, context.getEnvironment());
   };
   return this;
}
Copy the code

Now we know that hold ServiceInstanceListSupplier RoundRobinLoadBalancer, and hold the DiscoveryClient ServiceInstanceListSupplier, From the official website of using- the-DiscoveryClient, you can see that DiscoveryClient can discover services

3.4 ServiceInstanceListSupplier structure

public DiscoveryClientServiceInstanceListSupplier(DiscoveryClient delegate, Environment environment) {
   this.serviceId = environment.getProperty(PROPERTY_NAME);
   resolveTimeout(environment);
   this.serviceInstances = Flux.defer(() -> Flux.just(delegate.getInstances(serviceId)))
         .subscribeOn(Schedulers.boundedElastic()).timeout(timeout, Flux.defer(() -> {
            logTimeout();
            return Flux.just(new ArrayList<>());
         })).onErrorResume(error -> {
            logException(error);
            return Flux.just(new ArrayList<>());
         });
}
Copy the code

ServiceInstanceListSupplier when instantiated structure by DiscoveryClient get target information service instance list

3.5 Load Algorithm obtaining target service instances

final AtomicInteger position;
Copy the code
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
    if (instances.isEmpty()) {
        if (log.isWarnEnabled()) {
            log.warn("No servers available for service: " + serviceId);
        }
        return new EmptyResponse();
    }
    // TODO: enforce order?
    int pos = Math.abs(this.position.incrementAndGet());
​
    ServiceInstance instance = instances.get(pos % instances.size());
​
    return new DefaultResponse(instance);
}
Copy the code

Maintain an AtomicInteger variable, each time + 1 and the number of service instances modulo, get the target machine, so as to achieve the polling load algorithm

3.6 summarize

To sum up, discover services through DiscoveryClient, obtain the list of target service instances, and then implement polling load through the modular algorithm