[享学Netflix] 三十八、Ribbon核心API源码解析:ribbon-core(一)
作者:互联网
软件设计有两种方式:一种方式是,使软件过于简单,明显没有缺陷;另一种方式是,使软件过于复杂,没有明显的缺陷。
–> 返回专栏总目录 <–
代码下载地址:https://github.com/f641385712/netflix-learning
目录
前言
上篇文章整体上对Ribbon做了介绍,可能有小伙伴的有和我一样的感觉:知道Ribbon它是做什么大,仅只是略懂略懂状态,一种不踏实之感。Java库的好处是它开源,大大降低了学习的难度(不用纯凭记忆,可以从设计脉络上整体把握)。
从本文起将对Ribbon从API源码出发,附以示例讲解,逐个击破的方式,一步步对Ribbon进行全面剖析。因Ribbon一时半会还找不到替代的技术,并且国内学习它的资料比较少,希望此系列文章帮助到你。
正文
ribbon-core
它是Ribbon的核心包,其它任何包均依赖于此。该Jar包内只定义了公共API,自己并不能单独work,所以你可以理解为它就是一公共抽象即可。
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-core</artifactId>
<!-- 版本号为上文约定的2.3.0版本,保持和Spring Cloud的依赖一致 -->
<version>${ribbon.version}</version>
</dependency>
从图中可以看出:core核心包里并没有任何loadbalance负载均衡的概念,并且也没有任何http的概念在里面,所以说core是一个高度抽象的包:和lb无关,和协议亦无关。
说明:Ribbon的源码对类的命名有个规范 -> 接口一定是以
I
开头的,如IClient、以及后面的IRule、IPing等
IClient
Ribbon
要想负载均衡,那必然需要有发送请求的能力,而该接口就是它最最最最为核心接口喽,其它的一切组件均围绕它来设计和打造,包括LB。
该接口表示可以执行单个请求的客户端:发送请求Request,获得响应Response,注意并没有绑定任何协议哦(http、tcp、udp、文件协议、本地调用都是阔仪的)。
public interface IClient<S extends ClientRequest, T extends IResponse> {
// 执行请求并返回响应
public T execute(S request, IClientConfig requestConfig) throws Exception;
}
core包里没提供此接口的任何实现,在Spring Cloud
下有如下实现:
ClientRequest
表示适用于所有通信协议的通用客户端请求对象。该对象是immutable
不可变的。
public class ClientRequest implements Cloneable {
// 请求的URI
protected URI uri;
protected Object loadBalancerKey = null;
// 是否是可重试。true:该请求可重试 false:该请求不可重试
protected Boolean isRetriable = null;
// 外部传进来的配置,可以覆盖内置的IClientConfig配置哦
protected IClientConfig overrideConfig;
... // 省略各种构造器
... // 生路各种get方法。注意:没有set方法,因为该实例不可变
// 判断该请求是否可以重试(重要)
public boolean isRetriable() {
return (Boolean.TRUE.equals(isRetriable));
}
...
// 使用新的URI创建一个**新的**ClientRequest
// 它会先用clone方法去克隆一个,若抛错那就new ClientRequest(this) new一个实例
// 推荐子类复写此方法,提供更多、更有效的实施。
public ClientRequest replaceUri(URI newURI) {
ClientRequest req;
try {
req = (ClientRequest) this.clone();
} catch (CloneNotSupportedException e) {
req = new ClientRequest(this);
}
req.uri = newURI;
return req;
}
}
core包里无子类。Spring Cloud
下继承图谱如下:
IResponse
客户端框架的响应接口,请注意它是一个接口,而Request请求是类哦。
public interface IResponse extends Closeable {
// 从响应中获得实体。若是Http协议,那就是Body体
// 因为和协议无关,所以这里只能取名叫Payload
public Object getPayload() throws ClientException;
public boolean hasPayload();
// 如果认为响应成功,则为真,例如,http协议的200个响应代码。
public boolean isSuccess();
public URI getRequestedURI();
// 响应头们
public Map<String, ?> getHeaders();
}
小细节:该接口并没有形如getStatus
获取响应状态码的方法,是因为它和协议无关,而响应状态码是Http的专属。
core包内并无此接口的实现,Spring Cloud
下的情况如下:
本地测试环境搭建
本着main方法是一切程序的入口,任何组件均可本地测试的原则,再加上Ribbon核心的设计本就是和协议无关的,所以关于Ribbon核心的讲解内容使用单元测试(非集成测试)的方式来完成,相信会更加有助于你对负载均衡的学习。
说明:本环境搭建聚焦于内核部分的测试,也就是ribbon-core
和ribbon-loadbalancer
,因为他俩均为协议无惯性、网络无惯性设计,因此均可通过本地方式达到测试目的,让一切均可测试。
- 自定义MyClient实现
/**
* 不具有负载均衡功能的一个Client
*
* @author yourbatman
* @date 2020/3/14 22:09
*/
public class MyClient implements IClient<ClientRequest, MyResponse> {
@Override
public MyResponse execute(ClientRequest request, IClientConfig requestConfig) throws Exception {
MyResponse response = new MyResponse();
response.setRequestUri(request.getUri());
return response;
}
}
- 自定义MyResponse实现
public class MyResponse implements IResponse {
private URI requestUri;
public void setRequestUri(URI requestUri) {
this.requestUri = requestUri;
}
@Override
public Object getPayload() throws ClientException {
return "ResponseBody";
}
@Override
public boolean hasPayload() {
return true;
}
// 永远成功
@Override
public boolean isSuccess() {
return true;
}
@Override
public URI getRequestedURI() {
return requestUri;
}
@Override
public Map<String, ?> getHeaders() {
return null;
}
@Override
public void close() throws IOException {
}
}
- 最基础测试用例展示:
@Test
public void fun1() throws Exception {
// client配置
IClientConfig clientConfig = DefaultClientConfigImpl.getClientConfigWithDefaultValues("YourBatman");
// Client客户端,用于发送请求(使用ClientConfig配置)
// 因为木有LB功能,所以要不要IClientConfig没什么关系
MyClient client = new MyClient();
// 执行请求,获得响应
MyResponse response = client.execute(createClientRequest(), null);
System.out.println(response.isSuccess());
}
控制台输出:true
。这边是一个最简的本地测试环境,后面会基于此展开更多测试case
配置key管理
作为一个开源的库,需要使用配置来提高其弹性,因此Ribbon也有一套属于自己的配置管理,core包里记录着它的核心API。它需要管理大量的可识别的配置key
,这就是将要介绍的Ribbon对key的管理方式。
IClientConfigKey
用于定义用于IClientConfig
使用的key,注意它仅是key,而非k-v。
// 注意:泛型接口
public interface IClientConfigKey<T> {
// 用于做Hash的字符串表现形式
public String key();
// key的类型,比如Integer.class
// 若不指定,会根据泛型类型自动判断出来
public Class<T> type();
}
对该接口简单粗暴的理解:含义同普通的Object key
。它有一个子类:CommonClientConfigKey
,记录着通用的一些key们(太多了,40+个)。
CommonClientConfigKey
它是一个抽象类,所以IClientConfigKey
均以它的匿名子类的形式出现。
public abstract class CommonClientConfigKey<T> implements IClientConfigKey<T> {
public static final IClientConfigKey<String> AppName = new CommonClientConfigKey<>("AppName"){};
public static final IClientConfigKey<String> Version = new CommonClientConfigKey<>("Version"){};
public static final IClientConfigKey<Integer> Port = new CommonClientConfigKey<>("Port"){};
... // 因为实在太多了,略
public static final IClientConfigKey<Integer> ConnectTimeout = new CommonClientConfigKey<Integer>("ConnectTimeout"){};
public static final IClientConfigKey<Integer> ReadTimeout = new CommonClientConfigKey<Integer>("ReadTimeout"){};
... // 因为实在太多了,略
// 此key是使用得最最最多的:通过配置的形式指定Server地址(可指定多个)
public static final IClientConfigKey<String> ListOfServers = new CommonClientConfigKey<String>("listOfServers") {};
}
说明:配置的含义是任何程序都不可忽视的一部分,所以关于每个key到底什么意思?默认值是什么?有何作用?这在后面配置章节专题里专门讲解
本类内部维护着非常多个public static
的常量,这样外面便可直接使用。但是再怎么多,也不可能涵盖所有,所以本类也提供了自定义构造key的方法:
CommonClientConfigKey:
// 根据字符串name,创建出一个IClientConfigKey实例
public static IClientConfigKey valueOf(final String name) {
// 先从keys缓存里看看是否已经有了,若已经存在就直接返回
for (IClientConfigKey key: keys()) {
if (key.key().equals(name)) {
return key;
}
}
// 若没有现成的,那就创建一个新的返回
return new IClientConfigKey() {
@Override
public String key() {
return name;
}
@Override
public Class type() {
return String.class;
}
};
}
支出这里的一个小"Bug":新建出来的key并没有放进缓存里,其实放进去是否会更好呢???
示例
@Test
public void fun1() {
IClientConfigKey key1 = CommonClientConfigKey.valueOf("YourBatman");
IClientConfigKey key2 = CommonClientConfigKey.valueOf("YourBatman");
System.out.println(key1.key());
System.out.println(key1 == key2);
}
控制台输出:
YourBatman
false
可见,同一个String类型的key构建两次,得到的是两个不同的IClientConfigKey
实例,这么处理大多数时候对内存是不够友好的,这就是为何上面我认为它这是个小“bug”的原因。
总结
本文牛刀小试,介绍了ribbon-core
核心包里面的最核心API:IClient
,以及搭建好了本地测试环境,这对后续加入负载均衡逻辑提供基础支持。另外我们知道Ribbon它对key的管理使用的是IClientConfigKey
接口抽象,并且使用CommonClientConfigKey
管理着内部可识别的40+个常用key供以方便使用。
关于ribbon-core
的核心API第一部分就先介绍到这,下文继续。。。
声明
原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭
。你也可【左边扫码/或加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介绍 — 客户端负载均衡器
标签:core,享学,Hystrix,Netflix,IClientConfigKey,源码,key,public 来源: https://blog.csdn.net/f641385712/article/details/104806463