spring gateway网关 动态路由源码解析

236 阅读2分钟

前言

在使用gateway网关的时候,我们不想一个个的配置具体的地址,那么我们就可以使用动态路由:lb://service,这种方式配合注册中心完成动态路由,接下来就解析具体的实现

ReactiveLoadBalancerClientFilter

这是全局过滤器,在GatewayReactiveLoadBalancerClientAutoConfiguration配置类中实例化,只处理lb://类似的请求

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
   URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
   String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
   if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
      return chain.filter(exchange);
   }
   URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
   String serviceId = requestUri.getHost();
   ```
return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {
   }
}
核心是choose方法,前面都是准备工作
private Mono<Response<ServiceInstance>> choose(Request<RequestDataContext> lbRequest, String serviceId,
      Set<LoadBalancerLifecycle> supportedLifecycleProcessors) {
   ReactorLoadBalancer<ServiceInstance> loadBalancer = this.clientFactory.getInstance(serviceId,
         ReactorServiceInstanceLoadBalancer.class);
   if (loadBalancer == null) {
      throw new NotFoundException("No loadbalancer available for " + serviceId);
   }
   supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
   return loadBalancer.choose(lbRequest);
}
主要是通过loadBalencerClientFactory获得loadBalencer这么个对象,调用它的choose方法。

LoadBalencerClientFactory

类图:

image.png 它的父类是NamedContextFactory,这是applicationContext的子容器,主要是用于在服务之间隔离的作用,他们的配置等是不会共享的,在后面我们我们就可以根据服务去自定义负载均衡策略。

重点来了:loadBalancer.choose()

RoundRobinLoadBalencer

轮询的负载均衡策略

public Mono<Response<ServiceInstance>> choose(Request request) {
   ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
         .getIfAvailable(NoopServiceInstanceListSupplier::new);
   return supplier.get(request).next()
         .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
serviceInstanceListSupplierProvider,这其实就是一个Factory

suplier的get()方法,获取serviceInstence,通过追踪源码发现,最终调用的是CachingServiceInstanceListSupplier的get()方法

@Override
public Flux<List<ServiceInstance>> get() {
   return serviceInstances;
}

这个值是在构造函数里面设置的

public CachingServiceInstanceListSupplier(ServiceInstanceListSupplier delegate, CacheManager cacheManager) {
   super(delegate);
   this.serviceInstances = CacheFlux.lookup(key -> {
      // TODO: configurable cache name
      Cache cache = cacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
      if (cache == null) {
         if (log.isErrorEnabled()) {
            log.error("Unable to find cache: " + SERVICE_INSTANCE_CACHE_NAME);
         }
         return Mono.empty();
      }
      List<ServiceInstance> list = cache.get(key, List.class);
      if (list == null || list.isEmpty()) {
         return Mono.empty();
      }
      return Flux.just(list).materialize().collectList();
   }, delegate.getServiceId()).onCacheMissResume(delegate.get().take(1))
         .andWriteWith((key, signals) -> Flux.fromIterable(signals).dematerialize().doOnNext(instances -> {
            Cache cache = cacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
            if (cache == null) {
               if (log.isErrorEnabled()) {
                  log.error("Unable to find cache for writing: " + SERVICE_INSTANCE_CACHE_NAME);
               }
            }
            else {
               cache.put(key, instances);
            }
         }).then());
}
先从缓存里面拿,拿不到在调DiscoveryClientServiceInstanceListSupplier.get()方法获取

DiscoveryClientServiceInstanceListSupplier

@Override
public Flux<List<ServiceInstance>> get() {
   return serviceInstances;
}
这还是一个成员变量,也是在构造函数里面被设置的
public DiscoveryClientServiceInstanceListSupplier(ReactiveDiscoveryClient delegate, Environment environment) {
   this.serviceId = environment.getProperty(PROPERTY_NAME);
   resolveTimeout(environment);
   this.serviceInstances = Flux
         .defer(() -> delegate.getInstances(serviceId).collectList().flux().timeout(timeout, Flux.defer(() -> {
            logTimeout();
            return Flux.just(new ArrayList<>());
         })).onErrorResume(error -> {
            logException(error);
            return Flux.just(new ArrayList<>());
         }));
}
delegate:ReactiveComposeDiscoveryClient

ReactiveComposeDiscoveryClient

@Override
public Flux<ServiceInstance> getInstances(String serviceId) {
   if (discoveryClients == null || discoveryClients.isEmpty()) {
      return Flux.empty();
   }
   List<Flux<ServiceInstance>> serviceInstances = new ArrayList<>();
   for (ReactiveDiscoveryClient discoveryClient : discoveryClients) {
      serviceInstances.add(discoveryClient.getInstances(serviceId));
   }
   return CloudFlux.firstNonEmpty(serviceInstances);
}

这个ReactiveDiscoveryClient ,我们看类的注释: Represents read operations commonly available to discovery services such as Netflix Eureka or consul.io. 这是给注册中心实现的接口,用于获取serviceInstances 我们用的是nacos image.png 经过这些弯弯绕绕,我们终于获取到服务列表了。 回到RoundRobinLoadBalencer的coose方法上来,接下来调了processInstanceResponse方法

private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
      List<ServiceInstance> serviceInstances) {
      //这里是实现了轮询
   Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);
   if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
      ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
   }
   return serviceInstanceResponse;
}
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);
}

这就是一个普通的轮询算法,每次用++pos % size

这里基本就分析完了,后面就把url拼装一下就结束了