软件在能够复用前必须先能用。
–> 返回专栏总目录 <–
代码下载地址:https://github.com/f641385712/netflix-learning
目录
前言
如果你已经了解过Netflix Hystrix
,那么你对Command命令模式并不陌生。同样的,Ribbon也采用了基于RxJava的命令模式来控制请求的执行过程,这便是本文将要讲解的核心内容:LoadBalancerCommand
。
正文
基于RxJava的命令模式,LoadBalancerCommand
b表示负载均衡命令,用于中转请求到负载均衡器ILoadBalancer
。它的相关类并不多,如下截图:
其中最重要的当属LoadBalancerCommand
这个API,在这之前,先对其基础类进行打点。
ServerOperation
本接口非常简单,就是一个函数式接口Function
而已:“吃”进去一个Server,吐出来一个Observable<T>
。提供基于rxjava的Observable支持
// 仅仅继承自Func1,暴露出结果泛型T
public interface ServerOperation<T> extends Func1<Server, Observable<T>> {}
它作为一个动作,作为入参传入LoadBalancerCommand#submit()
提交给命令处理。
ExecutionInfo
一个POJO,代表执行时信息。
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);
}
}
它主要用于作为参数传递给ExecutionListener
,从而使得调用者可以窥探到执行是的信息。
ExecutionInfoContext
它是LoadBalancerCommand
的一个内部类,记录执行时的上下文info信息。
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);
}
}
该上下文主要记录了一次请求执行时的上下文重试次数,最终生成一个ExecutionInfo
实例交给监听器,使用者可以完成个性化判断(比如限制一把:重试过多就不要再重试了)。
ExecutionContext (重要)
在每个负载均衡器执行开始时创建的上下文对象,包含负载均衡器的某些元数据和的可变状态数据。
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;
}
}
通过父、子上下文的设计,能保证每个listener
有它自己的ExecutionContext
,但是你也可以通过ExecutionContext#getGlobalContext()
来得到所有listener
全局共享的上下文。
ExecutionListener
负载均衡器在不同执行阶段调用的监听器。
// 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);
}
对于该接口并么有任何内置实现,它是留给使用者的一个钩子。
ExecutionContextListenerInvoker
可以认为它是一个工具类:用于在执行上下文ExecutionContext
上调用各种ExecutionListener
们。功能上特别像大家熟悉的Spring Boot里的SpringApplicationRunListeners
用于执行SpringApplicationRunListener
监听器。
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);
}
}
}
- 监听器是Ribbon留给开发者介入到负载均衡执行过程的钩子,比较重要(无任何内置实现)
- 每个Listener执行均由一个自己的上下文,线程安全
- 可通过外部化配置:
"listener." + className + ".disabled"
的方式禁用执行的监听器,设计上满足一定弹性
总结
关于Ribbon负载均衡命令:LoadBalancerCommand(一)基础类打点部分就先介绍到这。本文内容相对简单,主要为下文服务,敬请关注。
声明
原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭
。你也可【左边扫码/或加wx:fsx641385712】邀请你加入我的 Java高工、架构师 系列群大家庭学习和交流。
- [享学Netflix] 一、Apache Commons Configuration:你身边的配置管理专家
- [享学Netflix] 二、Apache Commons Configuration事件监听机制及使用ReloadingStrategy实现热更新
- [享学Netflix] 三、Apache Commons Configuration2.x全新的事件-监听机制
- [享学Netflix] 四、Apache Commons Configuration2.x文件定位系统FileLocator和FileHandler
- [享学Netflix] 五、Apache Commons Configuration2.x别样的Builder模式:ConfigurationBuilder
- [享学Netflix] 六、Apache Commons Configuration2.x快速构建工具Parameters和Configurations
- [享学Netflix] 七、Apache Commons Configuration2.x如何实现文件热加载/热更新?
- [享学Netflix] 八、Apache Commons Configuration2.x相较于1.x使用上带来哪些差异?
- [享学Netflix] 九、Archaius配置管理库:初体验及基础API详解
- [享学Netflix] 十、Archaius对Commons Configuration核心API Configuration的扩展实现
- [享学Netflix] 十一、Archaius配置管理器ConfigurationManager和动态属性支持DynamicPropertySupport
- [享学Netflix] 十二、Archaius动态属性DynamicProperty原理详解(重要)
- [享学Netflix] 十三、Archaius属性抽象Property和PropertyWrapper详解
- [享学Netflix] 十四、Archaius如何对多环境、多区域、多云部署提供配置支持?
- [享学Netflix] 十五、Archaius和Spring Cloud的集成:spring-cloud-starter-netflix-archaius
- [享学Netflix] 十六、Hystrix断路器:初体验及RxJava简介
- [享学Netflix] 十七、Hystrix属性抽象以及和Archaius整合实现配置外部化、动态化
- [享学Netflix] 十八、Hystrix配置之:全局配置和实例配置
- [享学Netflix] 十九、Hystrix插件机制:SPI接口介绍和HystrixPlugins详解
- [享学Netflix] 二十、Hystrix跨线程传递数据解决方案:HystrixRequestContext
- [享学Netflix] 二十一、Hystrix指标数据收集(预热):滑动窗口算法(附代码示例)
- [享学Netflix] 二十二、Hystrix事件源与事件流:HystrixEvent和HystrixEventStream
- [享学Netflix] 二十三、Hystrix桶计数器:BucketedCounterStream
- [享学Netflix] 二十四、Hystrix在滑动窗口内统计:BucketedRollingCounterStream、HealthCountsStream
- [享学Netflix] 二十五、Hystrix累计统计流、分发流、最大并发流、配置流、功能流(附代码示例)
- [享学Netflix] 二十六、Hystrix指标数据收集器:HystrixMetrics(HystrixDashboard的数据来源)
- [享学Netflix] 二十七、Hystrix何为断路器的半开状态?HystrixCircuitBreaker详解
- [享学Netflix] 二十八、Hystrix事件计数器EventCounts和执行结果ExecutionResult
- [享学Netflix] 二十九、Hystrix执行过程核心接口:HystrixExecutable、HystrixObservable和HystrixInvokableInfo
- [享学Netflix] 三十、Hystrix的fallback回退/降级逻辑源码解读:getFallbackOrThrowException
- [享学Netflix] 三十一、Hystrix触发fallback降级逻辑的5种情况及代码示例
- [享学Netflix] 三十二、Hystrix抛出HystrixBadRequestException异常为何不会触发熔断?
- [享学Netflix] 三十三、Hystrix执行目标方法时,如何调用线程池资源?
- [享学Netflix] 三十四、Hystrix目标方法执行逻辑源码解读:executeCommandAndObserve
- [享学Netflix] 三十五、Hystrix执行过程集大成者:AbstractCommand详解
- [享学Netflix] 三十六、Hystrix请求命令:HystrixCommand和HystrixObservableCommand
- [享学Netflix] 三十七、源生Ribbon介绍 — 客户端负载均衡器
- [享学Netflix] 三十八、Ribbon核心API源码解析:ribbon-core(一)IClient请求客户端
- [享学Netflix] 三十九、Ribbon核心API源码解析:ribbon-core(二)IClientConfig配置详解
- [享学Netflix] 四十、Ribbon核心API源码解析:ribbon-core(三)RetryHandler重试处理器
- [享学Netflix] 四十一、Ribbon核心API源码解析:ribbon-core(四)ClientException客户端异常
- [享学Netflix] 四十二、Ribbon的LoadBalancer五大组件之:IPing心跳检测
- [享学Netflix] 四十三、Ribbon的LoadBalancer五大组件之:ServerList服务列表
- [享学Netflix] 四十四、netflix-statistics详解,手把手教你写个超简版监控系统
- [享学Netflix] 四十五、Ribbon服务器状态:ServerStats及其断路器原理
- [享学Netflix] 四十六、Ribbon负载均衡策略服务器状态总控:LoadBalancerStats
- [享学Netflix] 四十七、Ribbon多区域选择:ZoneAvoidanceRule.getAvailableZones()获取可用区
- [享学Netflix] 四十八、Ribbon服务器过滤逻辑的基础组件:AbstractServerPredicate
- [享学Netflix] 四十九、Ribbon的LoadBalancer五大组件之:ServerListFilter服务列表过滤器
- [享学Netflix] 五十、Ribbon的LoadBalancer五大组件之:ServerListUpdater服务列表更新器
- [享学Netflix] 五十一、Ribbon的LoadBalancer五大组件之:IRule(一)轮询和加权轮询
- [享学Netflix] 五十二、Ribbon的LoadBalancer五大组件之:IRule(二)应用于大规模集群的可配置规则
- [享学Netflix] 五十三、Ribbon的LoadBalancer五大组件之:IRule(三)随机和重试,所有IRule实现总结
- [享学Netflix] 五十四、Ribbon启动连接操作:IPrimeConnection检测Server是否能够提供服务
- [享学Netflix] 五十五、Ribbon负载均衡器执行上下文:LoadBalancerContext
- [享学Netflix] 五十六、Ribbon负载均衡器ILoadBalancer(一):BaseLoadBalancer
- [享学Netflix] 五十七、Ribbon负载均衡器ILoadBalancer(二):ZoneAwareLoadBalancer具备区域意识、动态服务列表的负载均衡器