1. 程式人生 > >一起寫RPC框架(十三)RPC服務提供端四--服務的降級

一起寫RPC框架(十三)RPC服務提供端四--服務的降級

  對於RPC而言,服務的降級也是必不可少的,何為服務的降級,就是在業務洪流來的時候,伺服器的壓力陡增,資料庫的壓力也很大的時候,輕量化服務的功效,比如某個非核心服務需要呼叫資料庫的,我們降級的服務不需要呼叫資料庫,就比如我們在某某電商購物的時候,商品詳情頁的側邊欄一般會有電商推薦的一些比較類似的產品,這個後臺的機制可能是某個推薦演算法,根據使用者瀏覽商品的記錄給出推薦的產品,這是非核心的邏輯,這個功能在伺服器的壓力比較大的時候,可以進行降級的處理,我們可以給出幾個預設的產品返回,因為推薦演算法可能會設計大資料的計算和分析,甚至設計幾次的資料庫查詢,在這個時候我們如果讓這個後臺方法預設返回幾個固定的值的時候,可以減輕服務的壓力,給其他的核心服務,例如支付,詳情頁等服務做出服務資源的讓步

服務降級對於RPC來說是可以或缺的,因為對於RPC來說,遠端呼叫是它的核心,但是服務降級對於服務治理是不可或缺的,實現服務降級的方式有很多種,本Demo RPC實現的方式比較簡單,提供一個Mock方法,這個Mock方法由我們自己實現:

舉例來說,對於一個簡單的介面而言比如HelloService來說:

package org.laopopo.example.demo.service;

/**
 * 
 * @author BazingaLyn
 * @description 
 * @time
 * @modifytime
 */
public interface HelloSerivce {
	
	String sayHello(String str);

}
我們先給出他正常邏輯的實現:
package org.laopopo.example.demo.service;

import org.laopopo.client.annotation.RPCService;

/**
 * 
 * @author BazingaLyn
 * @description Demo
 * @time 2016年8月19日
 * @modifytime
 */
public class HelloSerivceImpl implements HelloSerivce {

	@Override
	@RPCService(responsibilityName="xiaoy",
				serviceName="LAOPOPO.TEST.SAYHELLO",
				isVIPService = false,
				isSupportDegradeService = true,
				degradeServicePath="org.laopopo.example.demo.service.HelloServiceMock",
				degradeServiceDesc="預設返回hello")
	public String sayHello(String str) {
		
		//真實邏輯可能涉及到查庫
		return "hello "+ str;
		
	}

}
HelloServiceImpl.java這個類是我們一般的業務邏輯的實現,也許會設計到N次的庫的查詢,甚至還需要呼叫別人的介面,所以在業務洪流來的時候,這個服務佔據的資源過多,但這個服務也非核心的,這時最好的方法就是降級,而不是熔斷,服務的熔斷也是服務治理的一個手段,我們言歸正傳,降級的方法就是我們寫一個mock方法:
package org.laopopo.example.demo.service;

public class HelloServiceMock implements HelloSerivce {

	@Override
	public String sayHello(String str) {
		
		//直接給出預設的返回值
		return "hello";
	}

}

Mock方法就變得簡單的多,這樣就是一個服務降級的比較簡單的實現,接下來就是處理如何實現服務切換的功能了

當伺服器壓力不大的時候,我們一般呼叫HelloServiceImpl的方法,當伺服器的壓力大的時候,或者服務呼叫的成功率低於某個值的時候,就開始切換服務的提供物件,由HelloServiceImpl切換到HelloServiceMock上來,關於切換,簡單的又分兩種,手動和自動:

1)手動切換也很好理解,比如明天雙11,或者618,818之類的促銷節到來的時候,提前人工把某些非核心的功能,切換到Mock方法上來

2)自動切換也很好理解,比如某些服務相對比較重要,如果直接手動降級也是比較可惜,我們可以設定假如某個伺服器的服務呼叫成功率低於某個值的時候,就開始自動降級

我們看具體的實現:

我們在服務編織的時候,假如某個服務的Annotation上註明該服務有提供Mock類的時候,我們也進行簡單的編織:

//如果是支援服務降級服務,則需要根據降級方法的路徑去建立這個例項,並編制proxy
if(isSupportDegradeService){
	Class<?> degradeClass = null;
	try {
	degradeClass = Class.forName(degradeServicePath);
		Object nativeObj = degradeClass.newInstance();
		if(null  == globalProviderProxyHandler){
		        this.mockDegradeServiceProvider = nativeObj;
		}else{
			Class<?> globalProxyCls = generateProviderProxyClass(globalProviderProxyHandler, nativeObj.getClass());
			this.mockDegradeServiceProvider = copyProviderProperties(nativeObj, newInstance(globalProxyCls));
		}
	} catch (Exception e) {
		logger.error("[{}] class can not create by reflect [{}]",degradeServicePath,e.getMessage());
		throw new RpcWrapperException("degradeService path " + degradeServicePath +"create failed" ); 
        } 							
}
並將反射建立好的類與原物件一起編織成ServiceWrapper
ServiceWrapper serviceWrapper = new ServiceWrapper(serviceProvider,
						   mockDegradeServiceProvider,
						  serviceName,
						  responsiblityName,
						  methodName,
						  paramters,
						  isSupportDegradeService,
						  degradeServicePath,
						  degradeServiceDesc,
						  weight,
						  connCount,
						  isVIPService,
						  maxCallCount);
//放入到一個快取中,方便以後consumer來調取服務的時候,該來獲取對應真正的編織類
providerController.getProviderContainer().registerService(serviceName, serviceWrapper);


我們在服務呼叫的章節中說過,我們為每一個服務都配置了一個服務的狀態:
/**
<span style="white-space:pre">	</span> * 
<span style="white-space:pre">	</span> * @author BazingaLyn
<span style="white-space:pre">	</span> * @description 當前例項的服務狀態
<span style="white-space:pre">	</span> * @time 2016年8月29日
<span style="white-space:pre">	</span> * @modifytime
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>public static class CurrentServiceState {
<span style="white-space:pre">		</span>
<span style="white-space:pre">		</span>
<span style="white-space:pre">		</span>private AtomicBoolean hasDegrade = new AtomicBoolean(false);   // 是否已經降級
<span style="white-space:pre">		</span>private AtomicBoolean hasLimitStream = new AtomicBoolean(true); // 是否已經限流
<span style="white-space:pre">		</span>private AtomicBoolean isAutoDegrade = new AtomicBoolean(false); // 是否已經開始自動降級
<span style="white-space:pre">		</span>private Integer minSuccecssRate = 90; <span style="white-space:pre">				</span>// 服務最低的成功率,呼叫成功率低於多少開始自動降級


<span style="white-space:pre">		</span>public AtomicBoolean getHasDegrade() {
<span style="white-space:pre">			</span>return hasDegrade;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public void setHasDegrade(AtomicBoolean hasDegrade) {
<span style="white-space:pre">			</span>this.hasDegrade = hasDegrade;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public AtomicBoolean getHasLimitStream() {
<span style="white-space:pre">			</span>return hasLimitStream;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public void setHasLimitStream(AtomicBoolean hasLimitStream) {
<span style="white-space:pre">			</span>this.hasLimitStream = hasLimitStream;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public AtomicBoolean getIsAutoDegrade() {
<span style="white-space:pre">			</span>return isAutoDegrade;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public void setIsAutoDegrade(AtomicBoolean isAutoDegrade) {
<span style="white-space:pre">			</span>this.isAutoDegrade = isAutoDegrade;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public Integer getMinSuccecssRate() {
<span style="white-space:pre">			</span>return minSuccecssRate;
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>public void setMinSuccecssRate(Integer minSuccecssRate) {
<span style="white-space:pre">			</span>this.minSuccecssRate = minSuccecssRate;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>
<span style="white-space:pre">	</span>}
我們也維護了一個這樣的全域性變數:
private final ConcurrentMap<String, Pair<CurrentServiceState, ServiceWrapper>> serviceProviders = new ConcurrentHashMap<String, Pair<CurrentServiceState, ServiceWrapper>>();
Key是serviceName也就是服務名,Value是一個鍵值對,每一個服務的編織類,對應一個服務的狀態

當呼叫的時候獲取到每個服務的CurrentServiceState:

//判斷服務是否已經被設定為自動降級,如果被設定為自動降級且有它自己的mock類的話,則將targetCallObj切換到mock方法上來
if(currentServiceState.getHasDegrade().get() && serviceWrapper.getMockDegradeServiceProvider() != null){
 <span style="white-space:pre">	</span>targetCallObj = serviceWrapper.getMockDegradeServiceProvider();
}

我們讀取到CurrentServiceState中的是否已經降級的欄位,如果是true,則將目標物件替換成Mock物件就OK了,其他的流程不變,這樣就可以實現一個簡單的人工降級了,我們只需要手動修改hasDegrade這個欄位的值就可以完成服務的降級和恢復了

關於自動降級,其實就是在手動的基礎上加一個定時輪詢的檢查就可以了,比如每個一分鐘查詢一下或者統計一下此時服務呼叫的成功率,如果低於某個臨界值,例如成功率低於90%,則將hasDegrade欄位修改成true

this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

			@Override
			public void run() {
				//檢查是否有服務需要自動降級
				DefaultProvider.this.providerController.checkAutoDegrade();
			}
		}, 30, 60, TimeUnit.SECONDS);
public void checkAutoDegrade() {

		//獲取到所有需要降級的服務名
		List<Pair<String, CurrentServiceState>> needDegradeServices = providerContainer.getNeedAutoDegradeService();

		//如果當前例項需要降級的服務列表不為空的情況下,迴圈每個列表	
		if (!needDegradeServices.isEmpty()) {
			
			for (Pair<String, CurrentServiceState> pair : needDegradeServices) {

				//服務名
				String serviceName = pair.getKey();
				//最低成功率
				Integer minSuccessRate = pair.getValue().getMinSuccecssRate();
				//呼叫的實際成功率
				Integer realSuccessRate = ServiceMeterManager.calcServiceSuccessRate(serviceName);
				
				if (minSuccessRate > realSuccessRate) {
					
					final Pair<CurrentServiceState, ServiceWrapper> _pair = this.defaultProvider.getProviderController().getProviderContainer()
							.lookupService(serviceName);
					CurrentServiceState currentServiceState = _pair.getKey();
					if (!currentServiceState.getHasDegrade().get()) {
						currentServiceState.getHasDegrade().set(true);
					}
				}
			}
		}
	}
這樣就可以完成了一個比較簡單的服務降級了


相關推薦

一起RPC框架十三RPC服務提供--服務降級

  對於RPC而言,服務的降級也是必不可少的,何為服務的降級,就是在業務洪流來的時候,伺服器的壓力陡增,資料庫的壓力也很大的時候,輕量化服務的功效,比如某個非核心服務需要呼叫資料庫的,我們降級的服務不需要呼叫資料庫,就比如我們在某某電商購物的時候,商品詳情頁的側邊欄一般會有

一起RPC框架RPC之我所見

RPC 技術出來很多年了,出來的時候我估計還剛剛上大學,在國內,dubbo應該算是先驅者吧,下面的圖更是RPC架構經典中的經典 RPC在我的認知體系中,簡而言之,就是呼叫端,也可以稱之為消費者(Consumer)獲取到提供者的網路地址,並把方法呼叫的入參通過網路傳遞給P

RPC框架RPC簡介

一、概述 隨著公司規模的擴大,以及業務量的激增,單體應用逐步演化為服務/微服務的架構模式, 服務之間的呼叫大多采用rpc的方式呼叫,或者訊息佇列的方式進行解耦。幾乎每個大廠都會建立自己的rpc框架,或者基於知名的rpc框架進行改造。 目前, rpc

一起RPC框架十一RPC服務提供三--服務的呼叫

上一個小節簡單的介紹了服務提供者端如何去編制一個服務的資訊,然後將此服務的資訊傳送到註冊中心上去的基本過程了,其實算是比較簡單的,這節我們將簡單的介紹一些Consumer端呼叫Provider端的時候,Provider端是如何處理的 我們先確定一下遠端呼叫的幾個引數:

帶你手基於 Spring 的可插拔式 RPC 框架介紹

目錄: 帶你手寫基於 Spring 的可插拔式 RPC 框架(一)介紹 帶你手寫基於 Spring 的可插拔式 RPC 框架(二)整體結構 帶你手寫基於 Spring 的可插拔式 RPC 框架(三)通訊協議模組 帶你手寫基於 Spring 的可插拔式 RPC 框架(四)代理類的注入與服務啟動 帶你手寫基於 S

使用akka實現一個簡單的RPC框架

一、概述 目前大多數的分散式架構底層通訊都是通過RPC實現的,RPC框架非常多,比如前我們學過的Hadoop專案的RPC通訊框架,但是Hadoop在設計之初就是為了執行長達數小時的批量而設計的,在某些極端的情況下,任務提交的延遲很高,所有Hadoop的RPC顯得有些笨重。

RPC框架dubbo原始碼分析--dubbo呼叫過程分析

一、概述 消費端呼叫遠端服務介面時,使用上和呼叫普通的java介面是沒有任何區別,但是服務消費者和提供者是跨JVM和主機的,客戶端如何封裝請求讓服務端理解請求並且解析服務端返回的介面呼叫結果,服務端如何解析客戶端的請求並且向客戶端返回呼叫結果,這些框

【遠端呼叫框架】如何實現一個簡單的RPC框架優化三:軟負載中心設計與實現

【如何實現一個簡單的RPC框架】系列文章: 1.前言 在部落格【遠端呼叫框架】如何實現一個簡單的RPC框架(一)想法與設計中我們介紹了“服務註冊查詢中心”,負責服務資訊的管理即服務的註冊以及查詢,在目前為止的實現中,我們採用web應用的方式,以

RPC框架dubbo原始碼分析--dubbo服務提供者初始化

一、概述 dubbo服務提供者由dubbo:service來定義,從前面可以看到,Spring把dubbo:service解析成一個ServiceBean,ServiceBean實現了ApplicationListener和InitializingB

【遠端呼叫框架】如何實現一個簡單的RPC框架優化一:利用動態代理改變使用者服務呼叫方式

【如何實現一個簡單的RPC框架】系列文章: 這篇部落格,在(一)(二)的基礎上,對第一版本實現的服務框架進行改善,不定期更新,每次更新都會增加一個優化的地方。 1、優化一:利用動態代理改變使用者服務呼叫方式 1.1 目的 改變使用者

教你Http框架——三個樣例帶你深入理解AsyncTask

func implement oncreate 其它 層疊 worker dcl 例如 人員 這個標題大家不要奇怪,扯Http框架怎麽扯到AsyncTask去了,有兩個原因:首先是Http框架除了核心http理論外。其技術實現核心也是線程池 + 模板 +

服務框架十三Spring Boot Logstash日誌採集

  此係列文章將會描述Java框架Spring Boot、服務治理框架Dubbo、應用容器引擎Docker,及使用Spring Boot整合Dubbo、Mybatis等開源框架,其中穿插著Spring Boot中日誌切面等技術的實現,然後通過gitlab-CI以持續整合為Docker映

Java Executor併發框架十三Executor框架執行緒池關於異常的處理

關於為什麼要寫這篇文章,是因為我對Executor執行緒池的兩種提交任務的方式的不同產生的好奇,我們知道,可以通過execute和submit兩種方式往執行緒池提交我們的任務,但是這兩種任務提交的方式到底有什麼區別呢?通過execute方式提交的任務,我們不能獲取任務執行後的返回值,而通過submit提交

Android開發之手把手教你ButterKnife框架

系列文章目錄導讀: 一、概述 JakeWharton我想在Android界無人不知,無人不曉的吧, ButterKnife這個框架就是出自他隻手。這個框架我相信很多人都用過,本系列部落格就是帶大家更加深入的認識這個框架,ButterKnife截至目前

教你Http框架

大家都知道,從本質上來說app實際只是整個大系統當中的View層,因為設計優秀的系統中,app基本都不會承擔任何實際的業務邏輯處理,只是負責向用戶展示資料以及從使用者端蒐集使用者資料。而這個資料的一來一去,自然離不開網路通訊協議,而我們用得最多的,自然是這個ht

Android開發之手把手教你ButterKnife框架

系列文章目錄導讀: 一、概述 然後在Processor裡生成自己的程式碼,把要輸出的類,通過StringBuilder拼接字串,然後輸出。 try { // write the file JavaFileObject

從零開始JavaScript框架

1. 模組的定義和載入 1.1 模組的定義 一個框架想要能支撐較大的應用,首先要考慮怎麼做模組化。有了核心和模組載入系統,外圍的模組就可以一個一個增加。不同的JavaScript框架,實現模組化方式各有不同,我們來選擇一種比較優雅的方式作個講解。 先

從零一個編譯器十三:程式碼生成之遍歷AST

專案的完整程式碼在 C2j-Compiler 前言 在上一篇完成對JVM指令的生成,下面就可以真正進入程式碼生成部分了。通常現代編譯器都是先把生成IR,再經過程式碼優化等等,最後才編譯成目標平臺程式碼。但是時間水平有限,我們沒有IR也沒有程式碼優化,就直接利用AST生成Java位元組碼 入口 進行程式碼生

服務框架Docker映象化Dubbo微服務

  此係列文章將會描述Java框架Spring Boot、服務治理框架Dubbo、應用容器引擎Docker,及使用Spring Boot整合Dubbo、Mybatis等開源框架,其中穿插著Spring

MVC框架----前端與後MVC、MVVM等設計模式區別與聯絡

        上篇文章中提到了前端的框架分類,其中前端JS框架中有些是MVC設計模式,但是java和dotNET平臺也有自己的MVC也有自己的設計模式,這兩類有什麼區別呢,好多猿們甚是不解,旁徵博引