我们经常会重构一些复杂的接口,那么对于返回字段多并且逻辑复杂的接口如何来验证?
有如下几种方案
- 重新设计,重新设计前端的展示逻辑、后端的查询计算逻辑。然后进行重写(最优的方案)。
- 假如前端不配合重新搞。要求后端返回的数据要和重构前的一模一样,包括数据结构等等。这个时候要怎么做呢?
2.1. 对于这种情况下面,我们第一想到的肯定就是不重构。但是在不得不重构的时候我们要怎么去重构以及重构完怎么去测试验证?
首先:我们从重构的开发前的设计阶段入手。
-
首先我们重构的这个接口非常复杂。所以我们就将这个整体特别复杂的接口进行拆分,拆分为n个小逻辑串行的来处理。来保证代码的可读性。所以说这个时候我们可以使用pipeline设计模式来处理,入下图,每一个valve里面来处理相应的逻辑。
- ok现在我们已经知道怎么去开发了。然后开发完我们怎么去验证是否正确呢,有上千个字段,并且验证case很多?
2.1 这个时候就回到我们的正题了。流量回放
2.2 流量回放的概念就是将线上的真实流量进行回放一次,要对于正常的业务逻辑无感知的。(并且要保证时效性)。
现在是A服务上面有个接口要重构到B服务上面。我们这个流量回放该怎么做?
- 我们先新建一个服务C。
- 再A服务中需要重构的这个接口后面加一个发消息的kafka。将请求的参数以及返回的result。全部发到kafka中。
- 然后我们C服务来监听这个kafka消息。每当这个kafka过来了。去请求一下B服务重构后的接口。拿到返回值。然后进行返回值的json递归对比。将对比结果插入的数据库。进行观察,修改即可。
- 当对比结果都没有差异的时候,并且已经使用线上数据进行对比了很长时间。那么这个时候我们就可以放心的切流了。将流量切到新的接口。
下面是整个流程图
json递归对比代码
/**
* json比较
*/
public static StringBuffer compareJsonObject(JSONObject aJsonObject, JSONObject bJsonObject, String keyy, List list) {
StringBuffer sb = new StringBuffer();
Iterator 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对比不同,字段 = ").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中缺失此对象,对象=").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 list) {
//进行排序
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("对比服务排序异常",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中缺失此对象,对象=").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;
}