前言
在使用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
类图:
它的父类是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
经过这些弯弯绕绕,我们终于获取到服务列表了。
回到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拼装一下就结束了