前言

本篇為Tars專案上半程程式設計實踐的測試結果,經過上半程的原始碼學習、程式設計探索,現已初步實現Subset流量路由的三個核心功能:按比例路由、按引數路由與無規則路由。下面將介紹任務需求、測試模擬方案以及具體的測試結果。


1. 任務介紹

下圖為Subset流量管理任務需求 - 各語言SDK實現部分:

本人的任務是:使用Java語言實現Subset路由規則,核心點在新增三種模式的路由規則:

  • 按比例路由;
  • 按引數路由,分為精確路由與正則路由;
  • 無規則路由;

具體流程是

  • 獲取路由規則引數;
  • 獲取當前存活的服務節點;
  • 對節點進行Subset規則過濾【核心】;
  • 將過濾後的節點資訊存入status傳入下游;

現在,在中期彙報前已完成初步編碼,實現上述三種路由方式,下面將介紹筆者設計的測試方案,展示完成度

2. 測試模擬方案

測試採用Java SpringBoot的單元測試功能;

測試思路是先模擬建立前置條件(新增路由規則與存活節點資訊),接著呼叫filterEndpointsBySubset()方法進行測試,最後做些格式化處理。在這過程中輸出過濾前後的節點資訊。因此測試方案分為以下六步:

  1. 新增路由規則;
  2. 新增過濾前節點資訊;
  3. 【輸出】輸出過濾前節點;
  4. 【核心】對存活節點按subset規則過濾;
  5. 對過濾後節點進行格式化處理;
  6. 【輸出】輸出過濾結果。

2.0 *前置工作

根據Java SpringBoot的單元測試規則,需要先構建一些測試用到的元件:

//通訊器配置項
CommunicatorConfig communicatorConfig = new CommunicatorConfig();
//查詢助手
QueryHelper queryHelper = new QueryHelper(new Communicator(communicatorConfig));
//服務代理配置項
ServantProxyConfig servantProxyConfig = new ServantProxyConfig("objectName");
//節點列表
List<EndpointF> endpointFList = new ArrayList<EndpointF>();
//存活的節點
Holder<List<EndpointF>> activeEp = new Holder<List<EndpointF>>(new ArrayList<EndpointF>());

2.1 新增路由規則

首先,根據任務需求文件說明書,可以知道新增路由規則的入參是類似下面JSON資料:

因此,筆者將上面資訊存入servantProxyConfig服務代理配置項裡,rule_data用一個map格式的資料來裝配。下面程式碼用來模擬上游解析後的JSON資料,給通訊器新增路由規則:

//新增按比例路由規則
servantProxyConfig.setRuleType("proportion");
servantProxyConfig.setObjectName("objectName");
Map<String,String> map = new HashMap<>();
map.put("v1","20");
map.put("v2","80");
//map.put("v3",80);
servantProxyConfig.setRuleData(map);

2.2 新增存活節點

在TarsJava裡,節點儲存在list集合裡,直接add即可:

//新增存活節點
endpointFList.add(new EndpointF("host1",1,2,3,4,5,6,"setId1",7,8,9,10,"v1"));
endpointFList.add(new EndpointF("host2",1,2,3,4,5,6,"setId2",7,8,9,10,"v1"));
endpointFList.add(new EndpointF("host3",1,2,3,4,5,6,"setId3",7,8,9,10,"v2"));
endpointFList.add(new EndpointF("host4",1,2,3,4,5,6,"setId4",7,8,9,10,"v2"));
endpointFList.add(new EndpointF("host5",1,2,3,4,5,6,"setId5",7,8,9,10,"v2"));
//endpointFList.add(new EndpointF("host5",1,2,3,4,5,6,"setId5",7,8,9,10,"v3")); activeEp.setValue(endpointFList);

2.3 【輸出】遍歷輸出當前存活節點

 //輸出過濾前節點資訊
System.out.println("過濾前節點資訊如下:");
for( EndpointF endpoint : endpointFList){
System.out.println(endpoint.toString());
}

2.4 【核心】對存活節點按subset規則過濾

這是核心點,核心方法為filterEndpointsBySubset()按Subset規則過濾節點,這是筆者寫的和測試的方法,它能夠根據上述三種規則過濾節點:

//對存活節點按subset規則過濾
StringBuilder value = queryHelper.filterEndpointsBySubset(activeEp, servantProxyConfig);

2.5 後續格式化處理

這步將對過濾後的節點進行格式化處理:

//後續格式化相關工作
if (value.length() < 1) {
System.out.println("value.length() < 1");
return;
}
value.insert(0, Constants.TARS_AT);
value.insert(0, servantProxyConfig.getSimpleObjectName());

2.6 【輸出】輸出過濾結果

這步輸出過濾後的節點資訊:

//輸出過濾結果
System.out.println("過濾後節點資訊如下:");
System.out.println("result = " + value.toString());

筆者的測試方案設計就這樣分為六步,以下具體測試例項都是按照這六步進行。

3. 按比例路由規則 - 單次測試

具體測試方案為:節點有20%概率路由到Subset欄位為v1的節點,有80概率路由到Subset欄位為v2的節點【詳情檢視新增路由規則】。

測試程式碼如下:

/**
* 按比例路由規則 - 單次測試
*/
@Test
public void testProportionOnce() { //新增按比例路由規則
servantProxyConfig.setRuleType("proportion");
servantProxyConfig.setObjectName("objectName");
Map<String,String> map = new HashMap<>();
map.put("v1","20");
map.put("v2","80");
servantProxyConfig.setRuleData(map); //新增存活節點
endpointFList.add(new EndpointF("host1",1,2,3,4,5,6,"setId1",7,8,9,10,"v1"));
endpointFList.add(new EndpointF("host2",1,2,3,4,5,6,"setId2",7,8,9,10,"v1"));
endpointFList.add(new EndpointF("host3",1,2,3,4,5,6,"setId3",7,8,9,10,"v2"));
endpointFList.add(new EndpointF("host4",1,2,3,4,5,6,"setId4",7,8,9,10,"v2"));
endpointFList.add(new EndpointF("host5",1,2,3,4,5,6,"setId5",7,8,9,10,"v2")); activeEp.setValue(endpointFList); //輸出過濾前節點資訊
System.out.println("過濾前節點資訊如下:");
for( EndpointF endpoint : endpointFList){
System.out.println(endpoint.toString());
} //對存活節點按subset規則過濾
StringBuilder value = queryHelper.filterEndpointsBySubset(activeEp, servantProxyConfig); //後續格式化相關工作
if (value.length() < 1) {
System.out.println("value.length() < 1");
return;
}
value.insert(0, Constants.TARS_AT);
value.insert(0, servantProxyConfig.getSimpleObjectName()); //輸出過濾結果
System.out.println("過濾後節點資訊如下:");
System.out.println("result = " + value.toString());
}

測試結果如下:



結果分析:

可以看出成功路由到v2節點,但還是無法體驗到概率問題,於是有了下面按比例路由規則 - 多次測試

4. 按比例路由規則 - 多次測試

具體測試方案為:迴圈10000000次,統計路由結果,判斷是否按比例路由。這裡設定的比例是v1:v2:v3 = 20:80:20。

測試程式碼如下:

/**
* 按比例路由規則 - 多次測試
*/
@Test
public void testProportionTimes() { //新增按比例路由規則
servantProxyConfig.setRuleType("proportion");
servantProxyConfig.setObjectName("objectName");
Map<String,String> map = new HashMap<>();
map.put("v1","20");
map.put("v2","80");
map.put("v3","20");
servantProxyConfig.setRuleData(map); //新增存活節點
endpointFList.add(new EndpointF("host1",1,2,3,4,5,6,"setId1",7,8,9,10,"v1"));
endpointFList.add(new EndpointF("host2",1,2,3,4,5,6,"setId2",7,8,9,10,"v2"));
endpointFList.add(new EndpointF("host3",1,2,3,4,5,6,"setId3",7,8,9,10,"v3")); activeEp.setValue(endpointFList); //迴圈times次
int times = 10000000;
int v1Times = 0;
int v2Times = 0;
int v3Times = 0;
int errTimes = 0;
for (int i = 0; i < times; i++) {
//對存活節點按subset規則過濾
StringBuilder value = queryHelper.filterEndpointsBySubset(activeEp, servantProxyConfig);
//獲取value的subset值
String subset = null;
if (value != null && value.length() > 3) {
subset = value.substring(value.length() - 2);
if("v1".equals(subset)){
v1Times++;
} else if("v2".equals(subset)){
v2Times++;
} else if("v3".equals(subset)){
v3Times++;
}
} else {
errTimes++;
} }
//輸出結果
System.out.println("一共迴圈次數:" + times);
System.out.println("路由到v1次數:" + v1Times);
System.out.println("路由到v2次數:" + v2Times);
System.out.println("路由到v3次數:" + v3Times);
System.out.println("路由異常次數:" + errTimes);
}

測試結果如下:

結果分析:

可以看出結果比例接近v1:v2:v3 = 20:80:20,更改比例對應結果改變,按比例路由方法測試成功。

5. 按引數路由規則測試

具體測試方案為:設定染色的key為uid123,路由規則為精確(uid123) / 正則匹配(uid12*),路由規則跟染色key匹配,則路由到設定的v1;如果路由規則跟染色key不匹配,則無法路由,丟擲錯誤資訊。

測試程式碼如下:

/**
* 按引數路由規則
*/
@Test
public void testParameterAccurate() { //新增按精確匹配路由規則
servantProxyConfig.setRuleType("parameter");
servantProxyConfig.setObjectName("objectName");
Map<String,String> map = new HashMap<>(); //精確匹配測試
map.put("equal","uid123"); //精確匹配
//map.put("equal","uid12"); //當染色key和請求的rule_type對不上時 //正則匹配測試
//map.put("match","uid12*"); //正則匹配
//map.put("match","*d123"); //正則匹配
//map.put("match","uid123"); //正則匹配
//map.put("match","*id12*"); //正則匹配 //map.put("match","*i12d*"); map.put("route","v1");
servantProxyConfig.setRuleData(map); //設定染色的key
servantProxyConfig.setRouteKey("uid123"); //新增存活節點
endpointFList.add(new EndpointF("host1",1,2,3,4,5,6,"setId1",7,8,9,10,"v1"));
endpointFList.add(new EndpointF("host2",1,2,3,4,5,6,"setId2",7,8,9,10,"v1"));
endpointFList.add(new EndpointF("host3",1,2,3,4,5,6,"setId3",7,8,9,10,"v2"));
endpointFList.add(new EndpointF("host4",1,2,3,4,5,6,"setId4",7,8,9,10,"v2")); activeEp.setValue(endpointFList); //輸出過濾前節點資訊
System.out.println("過濾前節點資訊如下:");
for( EndpointF endpoint : endpointFList){
System.out.println(endpoint.toString());
}
//對存活節點按subset規則過濾
StringBuilder value = queryHelper.filterEndpointsBySubset(activeEp, servantProxyConfig);
//後續格式化相關工作
if (value.length() < 1) {
System.out.println("找不到節點,value.length() < 1");
return;
}
value.insert(0, Constants.TARS_AT);
value.insert(0, servantProxyConfig.getSimpleObjectName()); //輸出過濾結果
System.out.println("過濾後節點資訊如下:");
System.out.println("result = " + value.toString()); }

測試結果如下:

結果分析:

經過測試,發現精確匹配路由成功;而正則匹配也能成功;當路由規則跟染色key不匹配時,則無法路由,輸出結果如下圖:

6. 按無路由規則測試

具體測試方案為:無路由規則比較簡單,直接傳入{"default" , "v3" }路由規則即可,預設路由到v3;

測試程式碼如下:

/**
* 按無路由規則
*/
@Test
public void testDefault() {
//新增按比例路由規則
servantProxyConfig.setRuleType("default");
servantProxyConfig.setObjectName("objectName");
Map<String,String> map = new HashMap<>();
map.put("default","v3"); servantProxyConfig.setRuleData(map); //新增存活節點
endpointFList.add(new EndpointF("host1",1,2,3,4,5,6,"setId1",7,8,9,10,"v1"));
endpointFList.add(new EndpointF("host2",1,2,3,4,5,6,"setId2",7,8,9,10,"v1"));
endpointFList.add(new EndpointF("host3",1,2,3,4,5,6,"setId3",7,8,9,10,"v2"));
endpointFList.add(new EndpointF("host4",1,2,3,4,5,6,"setId4",7,8,9,10,"v2"));
endpointFList.add(new EndpointF("host5",1,2,3,4,5,6,"setId5",7,8,9,10,"v2"));
endpointFList.add(new EndpointF("host5",1,2,3,4,5,6,"setId5",7,8,9,10,"v3")); activeEp.setValue(endpointFList); //輸出過濾前節點資訊
System.out.println("過濾前節點資訊如下:");
for( EndpointF endpoint : endpointFList){
System.out.println(endpoint.toString());
} //對存活節點按subset規則過濾
StringBuilder value = queryHelper.filterEndpointsBySubset(activeEp, servantProxyConfig); //後續格式化相關工作
if (value.length() < 1) {
System.out.println("value.length() < 1");
return;
}
value.insert(0, Constants.TARS_AT);
value.insert(0, servantProxyConfig.getSimpleObjectName()); //輸出過濾結果
System.out.println("過濾後節點資訊如下:");
System.out.println("result = " + value.toString());
}

測試結果如下:

結果分析:

按照預設的預設規則能成功路由到v3。


最後

新人制作,如有錯誤,歡迎指出,感激不盡!
歡迎關注公眾號,會分享一些更日常的東西!
如需轉載,請標註出處!