你必须懂也可以懂的微服务系列四:服务发现与负载均衡

1.系列文章

2.前言

你必须懂也可以懂的微服务系列一:服务注册一文中介绍了应用程序在启动过程中会将服务名服务ip端口注册到注册中心

你必须懂也可以懂的微服务系列三:服务调用一文中介绍了远程服务调用,在调用远程服务的时候首先根据服务名获取目标服务地址信息列表,再根据相关负载算法计算出目标服务地址。这其中就涉及到服务发现负载均衡内容,也是本篇文章将要介绍的内容

3. 服务发现

你必须懂也可以懂的微服务系列三:服务调用一文中讲解到会通过http方式发起远程调用并返回结果,其实在进行远程调用之间还会进行服务发现操作,用于获取服务机器信息,以便实现负载

3.1 服务选择

@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();
}
复制代码

首先通过LoadBalancerClientFactory创建负载均衡客户端,再通过负载均衡客户端获取目标服务实例信息

3.2 负载均衡客户端声明

@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);
}
复制代码

可以看到在创建负载均衡客户端时需要ServiceInstanceListSupplier对象,从命名就能猜出此对象用于获取服务实例列表信息

3.3 ServiceInstanceListSupplier声明

@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() {
   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;
}
复制代码

至此我们了解到RoundRobinLoadBalancer中持有ServiceInstanceListSupplier,ServiceInstanceListSupplier中又持有DiscoveryClient,从using-the-discoveryclient官网文档中可以了解到DiscoveryClient可以实现服务发现功能

3.4 ServiceInstanceListSupplier构造

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<>());
         });
}
复制代码

ServiceInstanceListSupplier在实例化构造时通过DiscoveryClient获取目标服务实例列表信息

3.5 负载算法获取目标服务实例

final AtomicInteger position;
复制代码
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);
}
复制代码

维护一个AtomicInteger变量,每次 + 1与服务实例数取模,得到目标机器,从而实现轮询负载算法

3.6 总结

如上介绍这么多,总结一句话就是通过DiscoveryClient进行服务发现,获取目标服务实例列表,然后再通过取模算法实现轮询负载

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享