[Learn to enjoy Netflix] fifty-eight, Ribbon load-balancing command: LoadBalancerCommand (a) RBI base class

Software must be able to use before reuse.

-> return column List <-
download Address: https://github.com/f641385712/netflix-learning

Foreword

If you already know too Netflix Hystrix, then you are no stranger to the Command command mode. The same, Ribbon also used to perform the process based on RxJava command mode to control a request, which is the core content of this article will explain: LoadBalancerCommand.


text

Based RxJava command modes, LoadBalancerCommandB represents the load balancing command for transfer request to the load balancer ILoadBalancer. It's not much related classes, the following screenshot:

Here Insert Picture Description

One of the most important was undoubtedly LoadBalancerCommandthe API, before that, the first RBI of its base class.


ServerOperation

This interface is very simple, it is a function interface Functiononly: "eat" into a Server, a spit Observable<T>. Observable-based rxjava of support

// 仅仅继承自Func1,暴露出结果泛型T
public interface ServerOperation<T> extends Func1<Server, Observable<T>> {}

As a movement, as a passing into the reference LoadBalancerCommand#submit()submitted to the command processing.


ExecutionInfo

A POJO, on behalf of the implementation of the information.

public class ExecutionInfo {

    private final Server server;
    // 在该Server上已经尝试过的总次数
    private final int numberOfPastAttemptsOnServer;
    // 已经尝试过的Server总个数
    private final int numberOfPastServersAttempted;

	// 构造器是私有化的。该静态方法用于创建实例
    public static ExecutionInfo create(Server server, int numberOfPastAttemptsOnServer, int numberOfPastServersAttempted) {
        return new ExecutionInfo(server, numberOfPastAttemptsOnServer, numberOfPastServersAttempted);
    }
}

It is mainly used as a parameter to pass ExecutionListener, so that the caller can be performed to snoop information.


ExecutionInfoContext

It is LoadBalancerCommandan internal class, context info record information when executed.

LoadBalancerCommand:

	class ExecutionInfoContext {
		// 当前Server,每次调用set方法,此值就会变哦~~~~
		// serverAttemptCount记录了当前请求已经换了几次Server了~~~
        Server      server;
        // 已经重试的Server总数(每次换台Server就+1)
        int         serverAttemptCount = 0; 
        // 这次请求的重试总数(包括相同Server,以及换Server,总之是个总数)
        int         attemptCount = 0;

        public ExecutionInfo toExecutionInfo() {
            return ExecutionInfo.create(server, attemptCount-1, serverAttemptCount-1);
        }
        public ExecutionInfo toFinalExecutionInfo() {
            return ExecutionInfo.create(server, attemptCount, serverAttemptCount-1);
        }
	}

The context is mainly a record request to the context number of retries when executed, generate a final ExecutionInfoinstance to the listener , the user can complete the personalization determination (such as a limitation: the retry would not have multiple retries).


ExecutionContext (important)

In each load balancer execution start context object is created, the metadata contains some of the load balancer and the status variable data.

public class ExecutionContext<T> {
	
	// 存储元数据的Map
	private final Map<String, Object> context;
	// ChildContext继承自ExecutionContext
	private final ConcurrentHashMap<Object, ChildContext<T>> subContexts;

	// 请求。requestConfig的优先级比clientConfig的高哦,具有“覆盖”效果
	private final T request;
    private final IClientConfig requestConfig;
    private final RetryHandler retryHandler;
    private final IClientConfig clientConfig;
	
	// 子上下文:它增加了一个属性ExecutionContext<T> parent;
	// 这样便可以让两个ExecutionContext关联起来
    private static class ChildContext<T> extends ExecutionContext<T> {
        private final ExecutionContext<T> parent;
        ChildContext(ExecutionContext<T> parent) {
            super(parent.request, parent.requestConfig, parent.clientConfig, parent.retryHandler, null);
            this.parent = parent;
        }
        @Override
        public ExecutionContext<T> getGlobalContext() {
            return parent;
        }
    }
	... // 省略构造器赋值
	
	// default访问权限的方法:获取子执行上下文
	// 没有就会new一个然后放在ConcurrentHashMap缓存起来。key就是obj
	ExecutionContext<T> getChildContext(Object obj) { ... }
	...
	// 获得指定name的value。一个简单的k-v而已
    public void put(String name, Object value) {
        context.put(name, value);
    }
    public Object get(String name) {
        return context.get(name);
    }
    // 先去requestConfig里找,若没找再去clientConfig里找
    public <S> S getClientProperty(IClientConfigKey<S> key) { ... }

	// 注意:子上下文ChildContext的该方法返回的是parent,所以就打通了
    public ExecutionContext<T> getGlobalContext() {
        return this;
    }
}

By the Father, Son context of design, to ensure that each listenerhas its ownExecutionContext , but you can also ExecutionContext#getGlobalContext()be obtained for all listenerglobally shared context.


ExecutionListener

Listeners call load balancer in different stages of implementation.

// I:input类型,给ExecutionContext用。这样ExecutionContext#getRequest()就能拿到这个请求对象
// O:负载均衡器执行的output输出
public interface ExecutionListener<I, O> {

	// 在执行即将开始时调用。
	public void onExecutionStart(ExecutionContext<I> context) throws AbortExecutionException;
	// 当服务器被选中,请求**将在服务器上执行时**调用。
	public void onStartWithServer(ExecutionContext<I> context, ExecutionInfo info) throws AbortExecutionException;

	// 当在服务器上执行请求时接收到异常时调用(这时木有Response,只有异常信息)
	public void onExceptionWithServer(ExecutionContext<I> context, Throwable exception,  ExecutionInfo info);
	// 执行正常,有response
	public void onExecutionSuccess(ExecutionContext<I> context, O response,  ExecutionInfo info);
	// 在**所有重试**后认为请求失败时调用
	// finalException:最终的异常。有可能包装着重试时的各种异常
	public void onExecutionFailed(ExecutionContext<I> context, Throwable finalException, ExecutionInfo info);
}

And it has no built-realization for this interface, it is left to the user of a hook.


ExecutionContextListenerInvoker

It can be considered a utility class: for the execution context ExecutionContextto call on a variety of ExecutionListenerthem. Function very much like the familiar Spring Boot Lane SpringApplicationRunListenersfor executing SpringApplicationRunListenerthe listener.

public class ExecutionContextListenerInvoker<I, O> {

	// 执行上下文
	private final ExecutionContext<I> context;
	// 执行时的监听器们
	private final List<ExecutionListener<I, O>> listeners;
	
	private final IClientConfig clientConfig;
	// key表示的是ExecutionListener实现类的全类名
	// value:IClientConfigKey。它的值是"listener." + key+ ".disabled"
	// 因为CommonClientConfigKey.valueOf()比较耗时,所以这里用了缓存
	private final ConcurrentHashMap<String, IClientConfigKey> classConfigKeyMap;
	
	... // 省略构造器
	
	
    public void onExecutionStart() {
        onExecutionStart(this.context);
    }
	public void onExecutionStart(ExecutionContext<I> context) {
		for (ExecutionListener<I, O> listener : listeners) {
			// 若这个Listener没有被禁用,那就执行它
			if (!isListenerDisabled(listener)) {
				// 请注意:这里的上下文使用的是子上下文哦
				// 所以保证了每个监听器均有一个自己的上下文,各有一份自己的数据,线程安全
				listener.onExecutionStart(context.getChildContext(listener));
			}
		}
	}
	
	... // 执行监听器其它方法的逻辑一毛一样,略

	// 判断一个监听器是否被禁用。可通过动态配置来开/关
	// "listener." + className + ".disabled":禁用
    private boolean isListenerDisabled(ExecutionListener<?, ?> listener) {
        if (clientConfig == null) {
            return false;
        } else {
            String className = listener.getClass().getName();
            IClientConfigKey key = classConfigKeyMap.get(className);
            if (key == null) {
                key = CommonClientConfigKey.valueOf("listener." + className + ".disabled");
                IClientConfigKey old = classConfigKeyMap.putIfAbsent(className, key);
                if (old != null) {
                    key = old;
                }
            }
            return clientConfig.getPropertyAsBoolean(key, false);
        }
    }
}
  1. Ribbon listener is left to the developers involved in the process of execution of hook load balancing, the more important (without any built-realization)
  2. Each Listener executed by its own context, thread safe
  3. May be configured by externally of: "listener." + className + ".disabled"a way to disable execution of the listener, a certain flexibility to meet the design

to sum up

About Ribbon Load Balancing command: LoadBalancerCommand (a) on the basis of class RBI first introduced to this part. This article is relatively simple, mainly for the following services, so stay tuned.
Dividing line

statement

The original is not easy, not easy to code word, thank you for your thumbs, collection, attention. 把本文分享到你的朋友圈是被允许的,但拒绝抄袭. You can also scan code [left / or add wx: fsx641385712] I invite you to join the Java Engineering, Architect Series family group learning and communication.
Past Featured

Published 362 original articles · won praise 531 · views 480 000 +

Guess you like

Origin blog.csdn.net/f641385712/article/details/105027272