軟件設計有兩種方式:一種方式是,使軟件過於簡單,明顯沒有缺陷;另一種方式是,使軟件過於複雜,沒有明顯的缺陷。
–> 返回專欄總目錄 <–
代碼下載地址: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介紹 — 客戶端負載均衡器