Turbine學習筆記
Turbine原理
Hystrix/stream
在微服務架構中,Hystrix提供的是一種熔斷機制,當分佈式系統中的一個服務出現問題時,Hystrix保證該系統不產生雪崩效應,而能繼續服務。Hystrix爲每一個依賴服務維護一個線程池(或者信號量),當線程池佔滿,該依賴服務將會立即拒絕服務而不是排隊等待。每個依賴服務都被隔離開來,Hystrix 會嚴格控制其對資源的佔用,並在任何失效發生時,執行失敗回退邏輯。簡單來說,Hystrix通過觀察者模式對服務狀態進行監聽,並將監聽信息存儲在Hystrix/stream中,或者更確切的說,對於每一個服務的Hystrix/stream
,Hystrix會不斷去監聽該服務,並將監聽信息保存下來。具體原理與實現方式在此不做考慮。具體信息如下:
Hystrix Dashboard
Hystrix Dashboard簡單來說,就是將Hystrix/stream中的信息以儀表盤的信息展示出來,如下:
Turbine概述
Turbine真正做的,就是將每一個(指定)服務的 Hystrix/stream中的狀態信息取出,並集中處理(計算與展示),應該說,它是具有自己獨立的調度的,服務(實例)發現,服務連接,數據聚合,數據輸出,共四個過程。
如上圖:Turbine首先通過***InstanceDiscovery*** 模塊獲取所有的實例信息(定期更新獲取),ConnectionManager***負責連接到實例,連接上實例後,便會有源源不斷的數據流發送給聚合器***Aggregator,之後,再傳送給需要的地方。
Turbine流程
啓動
turbine啓動的時候會調用TurbineInit
的init()
方法進行初始化
public static void init() {
/*省略其他·代碼*/
//調用start()方式,初始化實例發現
InstanceObservable.getInstance().start(PluginsFactory.getInstanceDiscovery());
}
public void start(InstanceDiscovery iDiscovery) {
/*省略其他·代碼*/
//每隔pollDelayMillis通過producer去拉取一次主機的信息
timer.schedule(producer, 0, pollDelayMillis.get());
}
private final TimerTask producer = new TimerTask() {
@Override
public void run() {
/*省略其他·代碼*/
for(InstanceObserver watcher: observers.values()) {
if(currentState.get().hostsUp.size() > 0) {
try {
//通知InstanceObserver主機是UP狀態
//watcher是個InstanceObserver接口的實例化對象
watcher.hostsUp(currentState.get().hostsUp);
}
}
}
}
};
實際上,Turbine啓動過程中,會啓用以上方法,將所有是UP
狀態的實例信息都記錄並交給***ConnectionManager***,由它負責連接實例。
數據聚合
數據聚合主要包含3個部分:
- TurbineDataMonitor:數據監聽,從實例處獲取指標
- TurbineDataDispatcher:派發器,將數據聚合後輸出到客戶端或者下游的數據監聽
- TurbineDataHandler:數據處理
InstanceMonitor
實例監聽類中的startMonitor()
方法如下:
public void startMonitor() throws Exception {
/*省略其他·代碼*/
@Override
public Void call() throws Exception {
try {
//初始化
init();
monitorState.set(State.Running);
while(monitorState.get() == State.Running) {
//實現
doWork();
}
}
});
}
其中***init()***方法如下:
private void init() throws Exception {
/*省略其他代碼*/
HttpGet httpget = new HttpGet(url);
}
***url***如下:
private InstanceMonitor(/*省略構造函數參數*/) {
/*省略其他·代碼*/
this.url = urlClosure.getUrlPath(host);
}
追本溯源:
/**
* Replaces any {placeholder} attributes in a url suffix using instance attributes
*
* e.x. :{server-port}/hystrix.stream -> :8080/hystrix.stream
*
* @param host instance
* @param url suffix
* @return replaced url suffix
*/
private String processAttributeReplacements(Instance host, String url) {
for (Map.Entry<String, String> attribute : host.getAttributes().entrySet()) {
String placeholder = "{"+attribute.getKey()+"}";
if (url.contains(placeholder)) {
url = url.replace(placeholder, attribute.getValue());
}
}
return url;
}
由此可見,init()方法正在的目的是根據實例的指標地址 hystrix.stream(hystrix/stream) 去獲取指標信息。
而在***dowork*** 方法中:
private void doWork() throws Exception {
/*省略其他·代碼*/
//向派發器中發送數據
boolean continueRunning = dispatcher.pushData(getStatsInstance(), list);
}
數據派發
在 TurbineDataDispatcher 中,有方法叫做 pushData() 如下:
public boolean pushData(final Instance host, final Collection<K> statsData) {
/*省略其他·代碼*/
for (final HandlerQueueTuple<K> tuple : eventHandlers.values()) {
//HandlerQueueTuple管道中添加數據
tuple.pushData(statsData);
}
return true;
}
HandlerQueueTuple 管道中的方法 pushData() 方法如下:
public void pushData(K data) {
/*省略其他·代碼*/
//往隊列中寫數據
boolean success = queue.writeEvent(data);
}
將數據寫進 HandlerQueueTuple 管道中後,需要將他們讀取到客戶端,該部分在方法 dowork() 中實現:
public void doWork() throws Exception {
/*省略其他·代碼*/
do {
//從隊列中讀取事件
K data = queue.readEvent();
if (data == null) {
numMisses++;
if (numMisses > 100) {
Thread.sleep(100);
numMisses = 0; // reset count so we can try polling again.
}
} else {
statsData.add(data);
numMisses = 0;
stopPolling = true;
}
}
while(!stopPolling);
try {
//通過事件處理器將數據輸出到客戶端
eventHandler.handleData(statsData);
} catch (Exception e) {
if(eventHandler.getCriteria().isCritical()) {
logger.warn("Could not publish event to event handler for " + eventHandler.getName(), e);
}
}
}
public void handleData(Collection<T> data) {
/*省略其他·代碼*/
//寫到stream中
writeToStream(data);
}
writeToStream() 方法最終將指標數據響應給瀏覽器。
Turbine使用
添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置信息
spring.application.name=hystrix-dashboard-turbine
server.port=8009
turbine.appConfig=hello-service,server-feign,service-ribbon#turbine監控的服務名稱,可以多個
turbine.aggregator.clusterConfig= default
turbine.clusterNameExpression= new String("default")
eureka.client.service-url.defaultZone=http://localhost:8081/eureka
在服務啓動類上添加如下註解開啓 Turbine:
@SpringBootApplication
@EnableHystrixDashboard
@EnableTurbine
將相應服務啓動,並輸入網址 http://localhost:8009/turbine/stream ,將出現如下界面:
信息查看
說明turbine/stream的本質是獲取多個服務的hystrix/stream
我們打開Dashboard,並在地址欄裏輸入 http://localhost:8009/turbine/stream ,並使用Apache的ab壓力測試工具測試:
結果如上,Hystrix Dashboard Wiki上詳細說明了圖上每個指標的含義,如下圖: