我們經(jīng)常會(huì)重構(gòu)一些復(fù)雜的接口救赐,那么對(duì)于返回字段多并且邏輯復(fù)雜的接口如何來驗(yàn)證?
有如下幾種方案
- 重新設(shè)計(jì)经磅,重新設(shè)計(jì)前端的展示邏輯泌绣、后端的查詢計(jì)算邏輯预厌。然后進(jìn)行重寫(最優(yōu)的方案)阿迈。
- 假如前端不配合重新搞。要求后端返回的數(shù)據(jù)要和重構(gòu)前的一模一樣仿滔,包括數(shù)據(jù)結(jié)構(gòu)等等。這個(gè)時(shí)候要怎么做呢犹芹?
2.1. 對(duì)于這種情況下面崎页,我們第一想到的肯定就是不重構(gòu)。但是在不得不重構(gòu)的時(shí)候我們要怎么去重構(gòu)以及重構(gòu)完怎么去測(cè)試驗(yàn)證腰埂?
首先:我們從重構(gòu)的開發(fā)前的設(shè)計(jì)階段入手。
-
首先我們重構(gòu)的這個(gè)接口非常復(fù)雜屿笼。所以我們就將這個(gè)整體特別復(fù)雜的接口進(jìn)行拆分,拆分為n個(gè)小邏輯串行的來處理休雌。來保證代碼的可讀性。所以說這個(gè)時(shí)候我們可以使用pipeline設(shè)計(jì)模式來處理,入下圖担扑,每一個(gè)valve里面來處理相應(yīng)的邏輯趣钱。
image.png - ok現(xiàn)在我們已經(jīng)知道怎么去開發(fā)了。然后開發(fā)完我們?cè)趺慈ヲ?yàn)證是否正確呢燕垃,有上千個(gè)字段井联,并且驗(yàn)證case很多利术?
2.1 這個(gè)時(shí)候就回到我們的正題了。流量回放
2.2 流量回放的概念就是將線上的真實(shí)流量進(jìn)行回放一次低矮,要對(duì)于正常的業(yè)務(wù)邏輯無感知的。(并且要保證時(shí)效性)被冒。
現(xiàn)在是A服務(wù)上面有個(gè)接口要重構(gòu)到B服務(wù)上面军掂。我們這個(gè)流量回放該怎么做轮蜕?
- 我們先新建一個(gè)服務(wù)C。
- 再A服務(wù)中需要重構(gòu)的這個(gè)接口后面加一個(gè)發(fā)消息的kafka蝗锥。將請(qǐng)求的參數(shù)以及返回的result跃洛。全部發(fā)到kafka中。
- 然后我們C服務(wù)來監(jiān)聽這個(gè)kafka消息终议。每當(dāng)這個(gè)kafka過來了汇竭。去請(qǐng)求一下B服務(wù)重構(gòu)后的接口。拿到返回值穴张。然后進(jìn)行返回值的json遞歸對(duì)比细燎。將對(duì)比結(jié)果插入的數(shù)據(jù)庫(kù)。進(jìn)行觀察皂甘,修改即可玻驻。
- 當(dāng)對(duì)比結(jié)果都沒有差異的時(shí)候,并且已經(jīng)使用線上數(shù)據(jù)進(jìn)行對(duì)比了很長(zhǎng)時(shí)間偿枕。那么這個(gè)時(shí)候我們就可以放心的切流了璧瞬。將流量切到新的接口。
下面是整個(gè)流程圖
image.png
json遞歸對(duì)比代碼
/**
* json比較
*/
public static StringBuffer compareJsonObject(JSONObject aJsonObject, JSONObject bJsonObject, String keyy, List<String> list) {
StringBuffer sb = new StringBuffer();
Iterator<String> aKeys = aJsonObject.keySet().iterator();// jsonObject.keys();
if (null == bJsonObject) {
sb.append("b中缺失字段,key=").append(keyy).append("的value為null渐夸。a中的value=").append(JSONObject.toJSONString(aJsonObject.keySet())).append(";");
return sb;
}
while (aKeys.hasNext()) {
String key = aKeys.next();
if (!(aJsonObject.get(key) instanceof JSONObject) &&
!(aJsonObject.get(key) instanceof JSONArray) &&
null != aJsonObject.get(key)) {
Object aFiled = aJsonObject.get(key);
if (null == bJsonObject.get(key)) {
if (list.contains(key)) {
continue;
}
sb.append("字段不同,字段 = ").append(key).append(",aValue = ").append(aFiled).append(";");
continue;
}
Object bFiled = bJsonObject.get(key);
if (aFiled instanceof Number) {
Number aFiledNumber = (Number) aFiled;
if (bFiled instanceof Number) {
Number bFiledNumber = (Number) bFiled;
if (aFiledNumber.doubleValue() * num == bFiledNumber.doubleValue() * num) {
continue;
}
}
}
if (!aFiled.toString().equals(bFiled.toString())) {
if (list.contains(key)) {
continue;
}
sb.append("a和b對(duì)比不同,字段 = ").append(key).append(",aValue = ").append(aFiled).append(",bValue = ").append(bFiled).append(";");
continue;
}
}
if (aJsonObject.get(key) instanceof JSONObject) {
if (null == bJsonObject.get(key)) {
if (list.contains(key)) {
continue;
}
sb.append("b中缺失此對(duì)象嗤锉,對(duì)象=").append(key).append(",aValue = ").append(JSONObject.toJSONString(aJsonObject.get(key))).append(";");
continue;
}
if (!(bJsonObject.get(key) instanceof JSONObject)) {
if (list.contains(key)) {
continue;
}
sb.append("b中的字段").append(key).append("不是JSONObject類型").append(",aValue = ").append(JSONObject.toJSONString(aJsonObject.get(key))).append(";");
continue;
}
JSONObject aInnerObject = (JSONObject) aJsonObject.get(key);
JSONObject bInnerObject = (JSONObject) bJsonObject.get(key);
sb.append(compareJsonObject(aInnerObject, bInnerObject, key, list));
} else if (aJsonObject.get(key) instanceof JSONArray) {
if (null == bJsonObject.get(key)) {
if (list.contains(key)) {
continue;
}
sb.append("b中缺失此集合,集合=").append(key).append(",aValue = ").append(aJsonObject.get(key)).append(";");
continue;
}
if (!(bJsonObject.get(key) instanceof JSONArray)) {
if (list.contains(key)) {
continue;
}
sb.append("b中的字段").append(key).append("不是JSONArray類型").append(",aValue = ").append(JSONObject.toJSONString(aJsonObject.get(key))).append(";");
continue;
}
JSONArray aInnerArray = (JSONArray) aJsonObject.get(key);
JSONArray bInnerArray = (JSONArray) bJsonObject.get(key);
sb.append(compareJsonArray(aInnerArray, bInnerArray, key, list));
}
}
return sb;
}
private static StringBuffer compareJsonArray(JSONArray aJsonArray, JSONArray bJsonArray, String key, List<String> list) {
//進(jìn)行排序
if (key.equals("price_item")){
aJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("bill_type_name")));
bJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("bill_type_name")));
}else if (key.equals("item")){
aJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("name")));
bJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("name")));
}else if (key.equals("spec_req_price_item")){
aJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("name")));
bJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("name")));
}else if (key.equals("porterage_address_item")){
aJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("k")));
bJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("k")));
}else if (key.equals("virtual_phone_info")){
try {
aJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("user_phone_no")));
bJsonArray.sort(Comparator.comparing(item -> ((JSONObject) item).getString("user_phone_no")));
}catch (Exception e){
log.error("對(duì)比服務(wù)排序異常",e);
}
}
StringBuffer sb = new StringBuffer();
if (aJsonArray != null) {
Iterator aIterator = aJsonArray.iterator();
if (null == bJsonArray) {
sb.append("b中的JSONArray為null,key = ").append(key).append(",aValue = ").append(JSONObject.toJSONString(aJsonArray)).append(";");
return sb;
}
Iterator bIterator = bJsonArray.iterator();
while (aIterator.hasNext()) {
Object aKey = aIterator.next();
if (!bIterator.hasNext()) {
if (list.contains(key)) {
continue;
}
sb.append("JSONArray b中缺失此集合墓塌,集合=").append(key).append(",aValue = ").append(JSONObject.toJSONString(aKey)).append(";");
continue;
}
Object bKey = bIterator.next();
if (aKey instanceof JSONObject) {
if (null == bKey) {
if (list.contains(key)) {
continue;
}
sb.append("JSONArray b中缺失此對(duì)象瘟忱,對(duì)象=").append(key).append(";");
continue;
}
if (!(bKey instanceof JSONObject)) {
sb.append("JSONArray b中的 字段").append(bKey).append("不是JSONObject類型;");
continue;
}
JSONObject aInnerObject = (JSONObject) aKey;
JSONObject bInnerObject = (JSONObject) bKey;
sb.append(compareJsonObject(aInnerObject, bInnerObject, key, list));
} else if (aKey instanceof JSONArray) {
JSONArray aInnerObject = (JSONArray) aKey;
if (null == aInnerObject) {
continue;
}
if (null == bKey) {
if (list.contains(key)) {
continue;
}
sb.append("JSONArray b中缺失此集合勾习,集合=").append(key).append(";");
continue;
}
if (!(bKey instanceof JSONArray)) {
sb.append("JSONArray b中的 字段").append(bKey).append("不是JSONArray類型;");
continue;
}
JSONArray bInnerObject = (JSONArray) bKey;
sb.append(compareJsonArray(aInnerObject, bInnerObject, key, list));
}
}
}
return sb;
}