SpringCloud's Zuul source code detailed notes

1. Introduction to Zuul

Zuul plays a very important role in the Spring Cloud microservice system - a service gateway, a JVM-based router and load balancer.

The basic use of Zuul and the introduction of Filter are not explained here. This article mainly introduces the principle of Zuul.

2. Zuul processing flow

The processing flow is as follows:


Request => ZuulHandlerMapping => ZuulController => ZuulServlet

The main receiving logic is in ZuulServlet, and the execution Filterlogic is executed in sequence according to the type of Filter, as follows:

try {
    preRoute();
} catch (ZuulException e) {
    error(e);
    postRoute();
    return;
}
try {
    route();
} catch (ZuulException e) {
    error(e);
    postRoute();
    return;
}
try {
    postRoute();
} catch (ZuulException e) {
    error(e);
    return;
}

The received code is clear. In fact, the function of the Zuul component is here, and the rest of the routing of the request is mainly performed by the Ribboncomponent. Therefore, the following is Ribbonan .

The logical processing of routing route()is Route Filtercarried out.

3. Route Filter

There are and in Route FilterZuul , some people say there is (the local ones will not pay attention).SimpleHostRoutingFilterRibbonRoutingFilterSendForwardFilter

3.1 SimpleHostRoutingFilter

When you configure routing, directly configure Urlit instead of serviceId, then it is used, and the SimpleHostRoutingFilteropposite is used RibbonRoutingFilter.

Main logic:

public Object run() {
	// 省略没用逻辑 ...
	
	String uri = this.helper.buildZuulRequestURI(request);
	this.helper.addIgnoredHeaders();

	try {
	   // forward 主要逻辑
		CloseableHttpResponse response = forward(this.httpClient, verb, uri, request, headers, params, requestEntity);
		setResponse(response);
	}
	catch (Exception ex) {
		throw new ZuulRuntimeException(ex);
	}
}

// 从返回就能看出来调用 httpClient 完成的http请求
private CloseableHttpResponse forward () {
// ...
	// forwardRequest
	CloseableHttpResponse zuulResponse = forwardRequest(httpclient, httpHost, httpRequest);
	return zuulResponse;

// ...
}

// 通过 httpClient 发请求
private CloseableHttpResponse forwardRequest(CloseableHttpClient httpclient,
			HttpHost httpHost, HttpRequest httpRequest) throws IOException {
	return httpclient.execute(httpHost, httpRequest);
}

Summary: Build a Request and then make a request through httpClient.

3.2 RibbonRoutingFilter

public Object run() {
	// ...
	// 构建请求上下文,其实就是保护一下参数,如serviceId, retryable, url, 原request等
	RibbonCommandContext commandContext = buildCommandContext(context);
	// forward
	ClientHttpResponse response = forward(commandContext);
	setResponse(response);
	return response;
}

protected ClientHttpResponse forward(RibbonCommandContext context) {
	// ...
	// 这里的重点转到 command 上了,主要逻辑都是 command 中执行
	RibbonCommand command = this.ribbonCommandFactory.create(context);
	ClientHttpResponse response = command.execute();
	return response;
}

4. Command

4.1 Inheritance relationship

HystrixCommand
RibbonCommand
	<- AbstractRibbonCommand 
		<- HttpClientRibbonCommand / RestClientRibbonCommand / OkHttpRibbonCommand

The main logic is AbstractRibbonCommandmedium , subclasses are different selections HttpClient, OkHttpand HttpURLConnection.

4.2 AbstractRibbonCommand

Because of inheritance, HystrixCommandthe run()method needs to be implemented, and the above call execute()is from the HystrixCommandmain logic needs run()to be implemented.

protected ClientHttpResponse run() throws Exception {
	final RequestContext context = RequestContext.getCurrentContext();

	// 根据不同实现创建不同的Request
	RQ request = createRequest();
	// 执行负载均衡逻辑,其中 client 是 ribbonCommandFactory.create 中置入的
	RS response = this.client.executeWithLoadBalancer(request, config);

	context.set("ribbonResponse", response);

	if (this.isResponseTimedOut()) {
		if (response != null) {
			response.close();
		}
	}

	return new RibbonHttpResponse(response);
}

4.3 RibbonCommandFactory

CommandFactoryObserve what class the client is and what to do.

ribbonCommandFactoryThe inheritance relationship of:

RibbonCommandFactory   
	<- AbstractRibbonCommandFactory   
		<- HttpClientRibbonCommandFactory
		   OkHttpRibbonCommandFactory
		   RestClientRibbonCommandFactory

Take the default as HttpClientRibbonCommandFactoryan example , the code is as follows:

public HttpClientRibbonCommand create(final RibbonCommandContext context) {
    // 获取降级处理
	ZuulFallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());
	// serviceId 是根据请求的url来比对配置的路由得到的
	final String serviceId = context.getServiceId();
	// clientFactory 根据 ServiceId 获取相关的组件,包括 IRule, IClientConfig, ILoadBalancer 等组件
	final RibbonLoadBalancingHttpClient client = this.clientFactory.getClient(
			serviceId, RibbonLoadBalancingHttpClient.class);
	client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
	// 不同类型的 Factory 会获取不同的 client,生成不同的 Command
	return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties, zuulFallbackProvider,
			clientFactory.getClientConfig(serviceId));
}

It can be seen clientthat RibbonLoadBalancingHttpClient, of course, other implementations will also correspond to different client.

4.4 Client

clientThe inheritance is more complicated, from the main inheritance:

LoadBalancerContext 
	<- AbstractLoadBalancerAwareClient 
		<- AbstractLoadBalancingClient 
			<- RibbonLoadBalancingHttpClient
			   OkHttpLoadBalancingClient

In 4.2 the this.client.executeWithLoadBalancerimplementation is:

// AbstractLoadBalancerAwareClient.executeWithLoadBalancer()
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    // 重试策略处理类,要判断是不是重试可以重写这个
    RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
    // 专门用于失败切换其他服务端进行重试的 Command
    LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
            .withLoadBalancerContext(this)
            .withRetryHandler(handler)
            .withLoadBalancerURI(request.getUri())
            .build();

    try {
        // 见下面分析
        return command.submit(
            new ServerOperation<T>() {
                @Override
                public Observable<T> call(Server server) {
                    URI finalUri = reconstructURIWithServer(server, request.getUri());
                    S requestForServer = (S) request.replaceUri(finalUri);
                    try {
                    	// 真实动作,执行 execute
                        return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                    } 
                    catch (Exception e) {
                        return Observable.error(e);
                    }
                }
            })
            .toBlocking()
            .single();
    } catch (Exception e) {
        Throwable t = e.getCause();
        if (t instanceof ClientException) {
            throw (ClientException) t;
        } else {
            throw new ClientException(e);
        }
    }
}

Here are two places to pay attention to:

  1. LoadBalancerCommand implementation
  2. execute() logic

First look execute(), LoadBalancerCommandseparate introduction

4.4.1 execute()

Take the default as RibbonLoadBalancingHttpClientan example . ( It RibbonLoadBalancingHttpClientwill be renamed ApacheHttpLoadBalancingClientbecause its sibling is called OkHttpLoadBalancingClientso it looks more symmetrical).

public RibbonApacheHttpResponse execute(RibbonApacheHttpRequest request,final IClientConfig configOverride) {
	// ...
	
	final HttpUriRequest httpUriRequest = request.toRequest(requestConfig);
	// delegate == httpClient, 是通过 createDelegate 创建的,那么 okHttp 的相应的地方就是 okHttp 对应的 client
	// 所以,这里就是发送普通的 http 请求
	final HttpResponse httpResponse = this.delegate.execute(httpUriRequest);
	return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
}

4.5 LoadBalancerCommand

The main logic is in submit, using rxJavathe features to retry, a lot of detailed code is deleted below, leaving the main retry logic.

public Observable<T> submit(final ServerOperation<T> operation) {
    // ...
    
    // 外层的 observable 为了不同目标的重试
    // selectServer() 是进行负载均衡,返回的是一个 observable,可以重试,重试时再重新挑选一个目标server
    Observable<T> o = selectServer().concatMap(server -> {
    	// 这里又开启一个 observable 主要是为了同机重试
    	Observable<T> o = Observable
	      .just(server)
	      .concatMap(server -> {
	          return operation.call(server).doOnEach(new Observer<T>() {
	          	 @Override
                 public void onCompleted() {
                 	// server 状态的统计,譬如消除联系异常,抵消activeRequest等
                 }
                 
                 @Override
                 public void onError() {
                 	// server 状态的统计,错误统计等
                 }
                 
                 @Override
                 public void onNext() {
                    // 获取 entity, 返回内容
                 }
	          });
	    })
	    // 如果设置了同机重试,进行重试
	    if (maxRetrysSame > 0) 
	        // retryPolicy 判断是否重试,具体分析看下面
	        o = o.retry(retryPolicy(maxRetrysSame, true));
	    return o;
    })
    
    // 设置了异机重试,进行重试
    if (maxRetrysNext > 0) 
        o = o.retry(retryPolicy(maxRetrysNext, false));
    
    return o.onErrorResumeNext(exp -> {
    	return Observable.error(e);
    });
}

The main retry logic is as above, but the details need to be analyzed:

  1. retryPolicy()
  2. selectServer()
  3. Target Server Status Record

4.5.1 retryPolicy

private Func2<Integer, Throwable, Boolean> retryPolicy(final int maxRetrys, final boolean same) {
    return new Func2<Integer, Throwable, Boolean>() {
        @Override
        public Boolean call(Integer tryCount, Throwable e) {
            if (e instanceof AbortExecutionException) {
                return false;
            }
			  // 重构总次数还是会放弃重试的
            if (tryCount > maxRetrys) {
                return false;
            }
            
            if (e.getCause() != null && e instanceof RuntimeException) {
                e = e.getCause();
            }
            
            // 判断 exception 是否进行重试,可以自定义 handler 进行定制化
            return retryHandler.isRetriableException(e, same);
        }
    };
}

4.5.2 selectServer

private Observable<Server> selectServer() {
    return Observable.create(new OnSubscribe<Server>() {
        @Override
        public void call(Subscriber<? super Server> next) {
            try {
                // 通过 loadBalancerContext.getServerFromLoadBalancer 来进行负载均衡
                Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   
                next.onNext(server);
                next.onCompleted();
            } catch (Exception e) {
                next.onError(e);
            }
        }
    });
}

loadBalancerContext.getServerFromLoadBalancerPerform load balancing to select the next request target. The entire method is relatively large and will not be listed. After listing the calling relationship, analyze the main logic classes.

loadBalancerContext.getServerFromLoadBalancer () 
	> lb.chooseServer() 

What actually works is the ILoadBalancer.chooseServermethod .

4.5.3 ILoadBalancer

ILoadBalancerInheritance relationship:

ILoadBalancer
	<- AbstractLoadBalancer
		<- BaseLoadBalancer
			<- DynamicServerListLoadBalancer
				<- ZoneAwareLoadBalancer

ILoadBalancerinterface:

public interface ILoadBalancer {
	void addServers(List<Server> newServers);
	Server chooseServer(Object key); // 主要逻辑
	void markServerDown(Server server);
	@Deprecated
	List<Server> getServerList(boolean availableOnly);
	List<Server> getReachableServers();
	List<Server> getAllServers();
}

BaseLoadBalancerThe class that implements the logic of load balancing DynamicServerListLoadBalanceradds ServerListdynamic functions, and the logic of load balancing is not supplemented.

BaseLoadBalancer.chooseServerMain logic code:

public Server chooseServer(Object key) {
    if (counter == null) {
        counter = createCounter();
    }
    counter.increment();
    if (rule == null) {
        return null;
    } else {
        try {
        	  // Rule 执行挑选逻辑
            return rule.choose(key);
        } catch (Exception e) {
            logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
            return null;
        }
    }
}

4.6 IRule

IRule
<- AbstractLoadBalancerRule
	<- ClientConfigEnabledRoundRobinRule // abstract
		<- BestAvailableRule // 最小连接优先轮询
		   PredicateBasedRule // abstract
		   		<- AvailabilityFilteringRule 
		   		<- ZoneAvoidanceRule
	<- RoundRobinRule
		<- WeightedResponseTimeRule
	<- RandomRule
	<- RetryRule

4.6.1 PredicateBasedRule

Judging whether to choose based on logical assertion Rule, the specific Predicateinheritance is as follows:

Predicate
	<- AbstractServerPredicate
		<- AvailabilityPredicate // 可用性判断
		   ZoneAvoidancePredicate // 区域选择
		   CompositePredicate    // 复合判断自身没有逻辑,组合其他 Predicate
		  

AvailabilityPredicate

public boolean apply(@Nullable PredicateKey input) {
    LoadBalancerStats stats = getLBStats();
    if (stats == null) {
        return true;
    }
    // 判断是否跳过
    return !shouldSkipServer(stats.getSingleServerStat(input.getServer()));
}
    
    
private boolean shouldSkipServer(ServerStats stats) {
	 // 如果处于不可用 或者 当前请求大于最大限制 时跳过该目标        
    if ((CIRCUIT_BREAKER_FILTERING.get() && stats.isCircuitBreakerTripped()) 
            || stats.getActiveRequestsCount() >= activeConnectionsLimit.get()) {
        return true;
    }
    return false;
}

ZoneAvoidancePredicate

public boolean apply(@Nullable PredicateKey input) {
	// ...
	// 选择出可用区域,具体逻辑在 ZoneAvoidanceRule 中解析
	Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
   if (availableZones != null) {
   		return availableZones.contains(input.getServer().getZone());
	} else {
	    return false;
	}
}

CompositePredicateCombinatorial logic assertions

CompositePredicate

// 使用多个 Predicate 组成判断的 And 逻辑链
// 类似 if xx && yy & oo 
Predicate<PredicateKey> chain = Predicates.<PredicateKey>and(primaryPredicates);

// 获取可用列表时使用到回退逻辑
public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {
    List<Server> result = super.getEligibleServers(servers, loadBalancerKey);
    Iterator<AbstractServerPredicate> i = fallbacks.iterator();
    // 当筛选下来的server个数不符合配置中的最小个数时,会进行回退重选,一直回退到符合要求或者没有回退逻辑
    while (!(result.size() >= minimalFilteredServers && result.size() > (int) (servers.size() * minimalFilteredPercentage))
            && i.hasNext()) {
        AbstractServerPredicate predicate = i.next();
        result = predicate.getEligibleServers(servers, loadBalancerKey);
    }
    return result;
}

// AbstractServerPredicate 上面 super.getEligibleServers 
public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {
    if (loadBalancerKey == null) {
        return ImmutableList.copyOf(Iterables.filter(servers, this.getServerOnlyPredicate()));            
    } else {
        List<Server> results = Lists.newArrayList();
        for (Server server: servers) {
            // 每个 server 经过逻辑断言进行判断进行筛选
            if (this.apply(new PredicateKey(loadBalancerKey, server))) {
                results.add(server);
            }
        }
        return results;            
    }
}

Three Predicatehave been introduced, back to the topic.

PredicateBasedRuleMain logic:

public Server choose(Object key) {
    ILoadBalancer lb = getLoadBalancer();
    // 基于逻辑断言进行轮询 Predicate 由子类决定
    Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
    if (server.isPresent()) {
        return server.get();
    } else {
        return null;
    }       
}
// AbstractServerPredicate
public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) {
	 // 过滤可用结果, getEligibleServers 上面已经解析
    List<Server> eligible = getEligibleServers(servers, loadBalancerKey);
    if (eligible.size() == 0) {
        return Optional.absent();
    }
    // 标准轮询
    return Optional.of(eligible.get(nextIndex.getAndIncrement() % eligible.size()));
}

4.6.2 AvailabilityFilteringRule

AvailabilityFilteringRuletarget availability polling

public Server choose(Object key) {
    int count = 0;
    Server server = roundRobinRule.choose(key);
    while (count++ <= 10) {
        // 逻辑判断
        if (predicate.apply(new PredicateKey(server))) {
            return server;
        }
        // 轮询
        server = roundRobinRule.choose(key);
    }
    return super.choose(key);
}

// 其中 predicate
// CompositePredicate 组合逻辑,这里只有 AvailabilityPredicate 可用性判断
predicate = CompositePredicate.withPredicate(new AvailabilityPredicate(this, null))
                .addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
                .build();

4.6.3 ZoneAvoidanceRule

ZoneAvoidanceRuleThere is no overriding choosemethod , so it is still inherited PredicateBasedRule, so the filtering logic is actually compositePredicate. getEligibleServers, and after the above analysis, getEligibleServersit is actually all servers make logical judgments and return the passed ones.

// Predicate 组合了 zonePredicate 和 availabilityPredicate
compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);

It can be seen that it is zonePredicatemainly availabilityPredicatethe logical judgment of and .

zonePredicateThe above analysis mainly callsZoneAvoidanceRule.getAvailableZones

// getAvailableZones 主要逻辑
for (Map.Entry<String, ZoneSnapshot> zoneEntry : snapshot.entrySet()) {
    String zone = zoneEntry.getKey();
    ZoneSnapshot zoneSnapshot = zoneEntry.getValue();
    int instanceCount = zoneSnapshot.getInstanceCount();
    // 没有实例 即排除
    if (instanceCount == 0) {
        availableZones.remove(zone);
        limitedZoneAvailability = true;
    } else {
        double loadPerServer = zoneSnapshot.getLoadPerServer();
        // 不可用率超过阀值 或者 区域本来就不可用,即排除
        if (((double) zoneSnapshot.getCircuitTrippedCount())
                / instanceCount >= triggeringBlackoutPercentage
                || loadPerServer < 0) {
            availableZones.remove(zone);
            limitedZoneAvailability = true;
        } else {
            // 过滤出 负载最高的几个区域 
            if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
                // they are the same considering double calculation
                // round error
                worstZones.add(zone);
            } else if (loadPerServer > maxLoadPerServer) {
                maxLoadPerServer = loadPerServer;
                worstZones.clear();
                worstZones.add(zone);
            }
        }
    }
}

// 没有排除 并且 最高负载没有超过限制,返回
if (maxLoadPerServer < triggeringLoad && !limitedZoneAvailability) {
    // zone override is not needed here
    return availableZones;
}
// 否则 随机排除一个负载高的区域
String zoneToAvoid = randomChooseZone(snapshot, worstZones);
if (zoneToAvoid != null) {
    availableZones.remove(zoneToAvoid);
}
return availableZones;

Here's the question: why exclude an area when there are exclusions even if the limit load is not exceeded?

4.6.4 RoundRobinRule

It is relatively simple, as follows:

public Server choose(ILoadBalancer lb, Object key) {
    if (lb == null) {
        log.warn("no load balancer");
        return null;
    }

    Server server = null;
    int count = 0;
    while (server == null && count++ < 10) {
        List<Server> reachableServers = lb.getReachableServers();
        List<Server> allServers = lb.getAllServers();
        int upCount = reachableServers.size();
        int serverCount = allServers.size();

        if ((upCount == 0) || (serverCount == 0)) {
            log.warn("No up servers available from load balancer: " + lb);
            return null;
        }

		  // 累加取模,标准轮询
        int nextServerIndex = incrementAndGetModulo(serverCount);
        server = allServers.get(nextServerIndex);
        
        // 非线程安全list,可能会导致size有了对应索引处元素没有同步过来
        if (server == null) {
            /* Transient. */
            Thread.yield();
            continue;
        }

        // 可用即返回,不然下一轮
        if (server.isAlive() && (server.isReadyToServe())) {
            return (server);
        }

        // Next.
        server = null;
    }

    // 超过10次没有获取到可用的server
    if (count >= 10) {
        log.warn("No available alive servers after 10 tries from load balancer: "
                + lb);
    }
    return server;
}

4.6.5 WeightedResponseTimeRule

// 这里会启动一个维持 使用响应时间计算比重系数 的任务 DynamicServerWeightTask
// 主要公式
// totalResponseTime 为所有server 平均响应时间的和,由下公式知,响应越快 weight 越大
// weight = totalResponseTime - ss.getResponseTimeAvg();
// weightSoFar += weight;
// finalWeights.add(weightSoFar); 
// 0 - maxTotalWeight 的概率假设是平均的,那么 weight 越大区间就越大被选中的概率就越大
// 如 Aw(10) Bw(30) Cw(40) Dw(20)
// weightSoFar: 10, 40, 80, 100
// 那么 0-10, 10-40, 40-80, 80-100 可以加 40-80区间最大,概率就越大
double randomWeight = random.nextDouble() * maxTotalWeight;
// pick the server index based on the randomIndex
int n = 0;
for (Double d : currentWeights) {
    if (d >= randomWeight) {
        serverIndex = n;
        break;
    } else {
        n++;
    }
}

4.7 DynamicServerListLoadBalancer

Inherited from BaseLoadBalancerwith BaseLoadBalancerthe difference is that it holds the ServerListobject to dynamically obtain the Server list.

4.7.1 ServerList

ServerList
	<- DynamicServerList
	<- DiscoveryEnabledNIWSServerList
public interface ServerList<T extends Server> {
    public List<T> getInitialListOfServers();
    public List<T> getUpdatedListOfServers();   
}
  1. DynamicServerList: periodically get from RouteStorea
  2. DiscoveryEnabledNIWSServerList: get Eurekafrom

4.8 Server Status

How to poll and how to select and filter has Server Statusbeen , let's see.

ServerStatsThe class records all the state of the server.

4.8.1 Determine whether to skip

The following is to judge whether to skip the server The above has been analyzed, of which stats.isCircuitBreakerTrippedis the key to the judgment

// AvailabilityPredicate
shouldSkipServer(ServerStats stats) {
	if ((CIRCUIT_BREAKER_FILTERING.get() && stats.isCircuitBreakerTripped()) 
                || stats.getActiveRequestsCount() >= activeConnectionsLimit.get()) {
        return true;
    }
    return false;
}
public boolean isCircuitBreakerTripped(long currentTime) {
    // 获取故障的到期时间点
    long circuitBreakerTimeout = getCircuitBreakerTimeout();
    if (circuitBreakerTimeout <= 0) {
        return false;
    }
    // 大于当前时间表示还在出于故障
    return circuitBreakerTimeout > currentTime;
}

private long getCircuitBreakerTimeout() {
    long blackOutPeriod = getCircuitBreakerBlackoutPeriod();
    if (blackOutPeriod <= 0) {
        return 0;
    }
    // 上次失败的时间点 + 需要断路的时间长度
    return lastConnectionFailedTimestamp + blackOutPeriod;
}

private long getCircuitBreakerBlackoutPeriod() {
    int failureCount = successiveConnectionFailureCount.get();
    int threshold = connectionFailureThreshold.get();
    // 小于阀值(默认3)即不断路
    if (failureCount < threshold) {
        return 0;
    }
    // diff 超过阀值的次数,最大16
    int diff = (failureCount - threshold) > 16 ? 16 : (failureCount - threshold);
    // blackOutSeconds 最大 2^16 * 基数时间
    int blackOutSeconds = (1 << diff) * circuitTrippedTimeoutFactor.get();
    // 再次进行限制,断路总时间不超过 maxCircuitTrippedTimeout.get
    if (blackOutSeconds > maxCircuitTrippedTimeout.get()) {
        blackOutSeconds = maxCircuitTrippedTimeout.get();
    }
    return blackOutSeconds * 1000L;
}

This is how to judge whether it is in an open circuit unavailable state. Let's see how some states get in.

4.8.2 Recording Status

LoadBalancerCommandstateful records

// 这里开始
loadBalancerContext.noteOpenConnection(stats);


@Override
public void onCompleted() {
     // 记录准确状态
     recordStats(tracer, stats, entity, null);
}

@Override
public void onError(Throwable e) {
	 // 记录错误状态
    recordStats(tracer, stats, null, e);
    logger.debug("Got error {} when executed on server {}", e, server);
    if (listenerInvoker != null) {
        listenerInvoker.onExceptionWithServer(e, context.toExecutionInfo());
    }
}

@Override
public void onNext(T entity) {
    this.entity = entity;
    if (listenerInvoker != null) {
        listenerInvoker.onExecutionSuccess(entity, context.toExecutionInfo());
    }
}      
                                            
private void recordStats(Stopwatch tracer, ServerStats stats, Object entity, Throwable exception) {
    tracer.stop();
    // 这里介绍
    loadBalancerContext.noteRequestCompletion(stats, entity, exception, tracer.getDuration(TimeUnit.MILLISECONDS), retryHandler);
}

// loadBalancerContext
public void noteOpenConnection(ServerStats serverStats) {
    if (serverStats == null) {
        return;
    }
    try {
        serverStats.incrementActiveRequestsCount();
    } catch (Exception ex) {
        logger.error("Error noting stats for client {}", clientName, ex);
    }            
}

// serverStats
// 各种记录
public void incrementActiveRequestsCount() {        
    activeRequestsCount.incrementAndGet();
    requestCountInWindow.increment();
    long currentTime = System.currentTimeMillis();
    lastActiveRequestsCountChangeTimestamp = currentTime;
    lastAccessedTimestamp = currentTime;
    if (firstConnectionTimestamp == 0) {
        firstConnectionTimestamp = currentTime;
    }
}

// loadBalancerContext
public void noteRequestCompletion(ServerStats stats, Object response, Throwable e, long responseTime, RetryHandler errorHandler) {
	if (stats == null) {
		return;
	}
    try {
        recordStats(stats, responseTime);
        RetryHandler callErrorHandler = errorHandler == null ? getRetryHandler() : errorHandler;
        if (callErrorHandler != null && response != null) {
            // 没有错误时,清除连续错误标识
            stats.clearSuccessiveConnectionFailureCount();
        } else if (callErrorHandler != null && e != null) {
            // 判断是否需要断路的exception
            if (callErrorHandler.isCircuitTrippingException(e)) {
                // 有错误时开始连续错误计数
                stats.incrementSuccessiveConnectionFailureCount();  
                // 增加错误数                  
                stats.addToFailureCount();
            } else {
            	// 非断路错误时清除连续标识
                stats.clearSuccessiveConnectionFailureCount();
            }
        }
    } catch (Exception ex) {
        logger.error("Error noting stats for client {}", clientName, ex);
    }            
}

// 退场专用
private void recordStats(ServerStats stats, long responseTime) {
	if (stats == null) {
		return;
	}
	// 活动请求数减一
    stats.decrementActiveRequestsCount();
    // 增加请求统计
    stats.incrementNumRequests();
    // 记录响应时间,有些负载策略需要响应时间
    stats.noteResponseTime(responseTime);
}

5. Review

5.1 Call path

// 调用路径
1.HandleMapping -> 2.ZuulController -> 3.ZuulServlet.service() -> 4.RibbonRoutingFilter ->
5.HystrixCommand.execute() -> 6.AbstractRibbonCommand.run() ->
7.RibbonLoadBalancingHttpClient.executeWithLoadBalance() ->
8.LoadBalancerCommand.submit() -> 9.RibbonLoadBalancingHttpClient.execute() ->
10.HttpClient.execute()

1-4 are relatively easy, 5 is to have a fuse effect, so it Hystrixis packaged with , the actual logic is completed by the corresponding Command, 7 is a different Command holds a corresponding Client, executed executeWithLoadBalance()in order to achieve the effect of load balancing and retry , this effect is handed over to 8. LoadBalancerCommandComplete, but LoadBalancerCommandit is only responsible for retry and load balancing. The specific execution of remote http requests is still completed by 9, and each BalancingClientholds a real client, such as: HttpClient, OKHttp, by these client executes.

5.2 Branching logic

5.2.1 Load Balancing

It analyzes selectServerthe , as well as the commonly used ILoadBalancertypes, corresponding to IRulethe implementation of real selection and load polling logic.

5.2.2 Status record

The server state is used in the selection logic of load polling, so the main logic of how to determine whether it is in the open circuit state is analyzed by analyzing the state record.

5.3 Summary

ZuulThe main code is not very large, that is, the request comes in and is Filterprocessed , and the logic of routing to the upstream server is all Ribbonlogic.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324990773&siteId=291194637