1. 程式人生 > >Dubbo服務治理之灰度釋出方案(版本釋出控制影響範圍)

Dubbo服務治理之灰度釋出方案(版本釋出控制影響範圍)

1、方案背景
   背景:基於Dubbo服務的治理,是否可以支援業務級別的灰度釋出、是否基於業務引數的路由轉發。例如以GIS為例,當釋出一個新版本時,是否可以以按照解析地址或合作伙伴來區分,版本釋出之初,只希望地址為:廣東省的解析請求傳送到新版本,而其他的地址請求還是使用舊版;或者根據合作伙伴例如UCP(優享寄)的請求轉發到新版本伺服器,其他合作伙伴還是轉發到舊版,達成業務級別的灰度釋出,控制新版本的影響範圍。例如OMS系統,可以根據合作伙伴,將重量級客戶的請求轉發到單獨的伺服器叢集,確保其高可用。
   本文將對上述議題結合Dubbo提供的功能,提出設計方案。
2、方案理論基礎
   Dubbo的服務呼叫原理圖:
在這裡插入圖片描述


   客戶端在發起RPC服務呼叫之前,在客戶端首先從伺服器列表中選擇一個服務呼叫者,包含如下關鍵角色:
   1、Directory
   服務的動態發現,通常基於註冊中心進行服務的動態註冊與發現,其具體實現類為RegistryDirectory。
   2、Router
   路由實現,其含義是根據Directory發現的所有服務提供者列表中,進行路由選擇,也就是根據一定的路由規則選擇合適的服務提供者,為Directory發現的服務提供者列表子集,可以基於Condition或指令碼(預設為JS指令碼,其實現類為ScriptRouter)。
   3、LoadBalance
   負載均衡機制,其作用主要是根據負載均衡演算法(隨機、輪詢)
等演算法,從(Directory–>Router)中返回的服務提供者列表中選擇一個服務提供者,進行本次的RPC服務呼叫。
4、Cluster
   叢集(容錯機制),就是當從服務提供者列表中按照負載均衡演算法選擇一個服務提供者,進行RPC服務呼叫後,傳送了異常後的策略,例如failover(重試)、failfast(快速失敗)等。
   服務的灰度釋出,其目標是希望根據請求,某些請求走新版本伺服器,某些請求走舊版本伺服器,其本質就是路由機制,即通過一定的條件來縮小服務的服務提供者列表,正好與Dubbo的Router相吻合。
   有關於Dubbo的Router機制,請參考官方文件第【46、47、48】頁,如果想從原始碼的角度瞭解其實現機制,請參考博文:
https://blog.csdn.net/prestigeding/article/details/80848594

   有了理論支援,下文將根據上述理論進行實戰。
3、方案具體實現示例
   本示例程式碼需要完成的任務是,對DemoService#createUser服務,其使用者機構ID(orgId)為1的走新版本(當前服務提取者列表的最後一臺伺服器),其他的請求走所有的伺服器(除最後一臺伺服器)。
在這裡插入圖片描述
在這裡插入圖片描述
   由於是需要基於請求引數,本文給出基於JS指令碼的路由機制,首先,當前版本的dubbo-admin可以後臺頁面維護基於條件表示式的路由規則,其介面如下:
在這裡插入圖片描述
在這裡插入圖片描述
   備註:並且當前dubbo-admin版本,並不支援基於JS表示式的路由規則,如果手動建立基於表示式的路由規則,其頁面將無法列出路由表示式,其介面如下:
在這裡插入圖片描述

   3.1 JS指令碼
   各個專案,各個服務需要根據自身的需求,定義如下指令碼:

/**
 * DemoService router,針對不同的方法,可能需要各自提供,主要是引數的獲取,不同的過濾規則
 * 針對引數進行路由過濾
 * 
 * 本示例針對 DemoSerivce# ResponseResult createUser(User user) 方法,根據user的orgId進行路由選擇
 * @param invokers
 * @param invocation
 * @param context
 * @returns
 */
function demoService_createUser_router(invokers, invocation, context) {
	if(invokers == null || invokers.size() < 1) {
		return invokers;
	}
	
	if(!"createUser".equals(invocation.getMethodName())) { // 如果方法不匹配,預設無條件通過該路由規則
		return invokers;
	}
	
	var availableInvokers = new java.util.ArrayList(invokers.size());
	for (var i=0;i<invokers.size(); i++) {    // 先選擇可用的服務提供者列表
		if(invokers.get(i).isAvailable()) {
			availableInvokers.add(invokers.get(i));
		}
	}
	
	var invArguments = invocation.getArguments();
	if(invArguments == null || invArguments.length == 0) { // 如果引數為空,無法根據引數進行路由選擇
		return availableInvokers; 
	}
	
	// 獲取需要進行路由的引數,這裡使用第一個引數 ,這裡各自根據各自的業務 進行獲取,本例項預設使用第一個引數
	var firstArgument = invArguments[0];
	var orgId = firstArgument == null ? "" : firstArgument.getOrgId();
	
	
	if(orgId == 1 || orgId == "1") { // 如果orgId == 1 ,只走最後一個節點,其餘的走其他節點
		var selectInvokers = new java.util.ArrayList(1);
		selectInvokers.add(availableInvokers.get(availableInvokers.size()-1));
		return selectInvokers;
	} else {
		var selectInvokers = new java.util.ArrayList(availableInvokers.size()-1);
		for(var i=0;i<availableInvokers.size()-1; i++) {
			selectInvokers.add(availableInvokers.get(i));
		}
		return selectInvokers;
	}
}

   3.2 向註冊中心註冊JS指令碼路由規則
   上文已經說明,目前的dubbo-admin不支援在介面上註冊路由規則,現給出基於JAVA程式碼來編寫註冊程式:

public static void main(String[] args) throws Exception{
	URL registryUrl = URL.valueOf("zookeeper://127.0.0.1:2181");
    ZookeeperRegistryFactory zookeeperRegistryFactory = new 
                       ZookeeperRegistryFactory();
   zookeeperRegistryFactory.setZookeeperTransporter(new 
        CuratorZookeeperTransporter());
   Registry zookeeperRegistry = (ZookeeperRegistry) 
             zookeeperRegistryFactory.createRegistry(registryUrl);
   URL routerURL = 
          URL.valueOf("script://0.0.0.0/com.alibaba.dubbo.demo.Demo
                Service?category=routers&dynamic=false&enabled=true&fo
                rce=false&name=demoService_createUser_router&priority=
          0&runtime=true");
   routerURL = routerURL.addParameter("rule", 
   URL.encode(get_demoService_createUser_router()));
   zookeeperRegistry.register(routerURL);     // 註冊
   // zookeeperRegistry.unregister(routerURL); // 取消註冊
}

   一旦執行上述程式碼,將會動態註冊URL,服務提供者無需重啟,下次服務呼叫後會自動生效(其背後原理是基於註冊中心的動態發現)。
上述示例程式碼,我已經在本地環境,已能成功執行,並達到預期效果,公司專案需要根據自身的特點,特別服務方法的引數(例如合作伙伴ID的獲取方式),以及路由需求來定製編寫其路由指令碼(js指令碼)。
   3.3 總結
上述展示了Dubbo服務基於業務灰度釋出的方案,以及基於合作伙伴的服務隔離機制(根據服務呼叫業務引數來決定服務呼叫者的篩選)。主要是展示了基於腳步的路由規則,其條件表示式的路由規則請參考其Demo,其核心理論支援是Dubbo提供的Router,在進行負載均衡前,根據路由規則對服務提供者列表進行篩選。