架構思維:構建高并發讀服務_基于流量回放實現讀服務的自動化測試回歸方案

文章目錄

  • 引言
  • 一、升級讀服務架構,為什么需要自動化測試?
  • 二、自動化回歸測試系統:整體架構概覽
  • 三、日志收集
    • 1. 攔截方式
    • 2. 存儲與優化策略
    • 3. 架構進化
  • 四、數據回放
    • 技術實現
    • 關鍵能力
  • 五、差異對比
    • 對比方式
    • 靈活配置
  • 六、三種回放模式詳解
    • 1. 離線回放
    • 2. 實時回放(對比新舊服務)
    • 3. 無錄制實時回放
  • 七、使用注意事項與最佳實踐
  • 八、模擬核心Code
  • 九、小結

在這里插入圖片描述

引言

在高并發讀服務的架構優化過程中,我們往往關注系統如何抗壓、如何緩存命中率更高,甚至在性能提升方案落實后迅速投入重構。然而,在這一過程中,容易被忽略的一環就是“測試回歸”。

接下來我們將從實際落地角度,系統性地介紹一種支持讀服務快速升級、業務穩定推進的「自動化測試回歸系統架構」方案,構建一套覆蓋全量場景、支持自助回歸的自動化測試體系。


一、升級讀服務架構,為什么需要自動化測試?

假設我們已落地了支持高并發的讀服務架構,包括懶加載緩存、全量緩存、數據同步機制等。但讀服務的升級改造帶來的“回歸壓力”卻是另一種挑戰:

  • 架構重構往往影響范圍廣,測試周期按“月”計。
  • 日常需求中即便僅修改部分接口邏輯,也可能因底層復用代碼影響其他未修改接口,造成線上 Bug。

新老版本的接口未變架構圖

在這里插入圖片描述

解決這類回歸測試痛點,最優解是:自動化測試系統。這不僅提升了測試效率,也為系統升級提供了「安全緩沖帶」。


二、自動化回歸測試系統:整體架構概覽

自動化測試回歸系統的核心由三個模塊組成:

  1. 日志收集:攔截線上請求,記錄請求參數和響應數據,生成“真實用戶用例”。
  2. 數據回放:基于收集的請求數據,自動向新舊服務發起請求,觸發真實的業務流程。
  3. 差異對比:將新老版本的響應結果進行對比,捕捉潛在 Bug。

在這里插入圖片描述


三、日志收集

基于過濾器的日志收集架構圖

在這里插入圖片描述

1. 攔截方式

  • HTTP 接口:基于 Spring 的 Interceptor 或 Servlet 的 Filter 攔截。
  • RPC 接口:攔截 RPC 框架底層通信邏輯(如 Dubbo 的 Filter)。

攔截的請求被封裝為統一格式:

{"應用名": "XXX","接口方法名": "/api/order/detail","入參": "{...}","出參": "{...}"
}

這些日志通過 MQ 推送至回歸平臺進行存儲與處理。

2. 存儲與優化策略

  • 接口元數據存儲在關系型數據庫
  • 大體量出入參數據存儲在如 HBase 等高吞吐的 NoSQL
  • 提供去重、清洗、采樣功能,避免數據爆炸性增長
  • 非業務環境如壓測數據需剔除

3. 架構進化

單獨進程的日志收集架構圖

在這里插入圖片描述

  • 同進程采集:輕量集成,但存在侵入性
  • 獨立進程采集:將日志打印至文件,由單獨進程監聽并推送 MQ,降低業務系統資源占用

四、數據回放

數據回放模塊模擬用戶請求,通過原始日志數據中的入參信息,重放請求以獲得當前版本的響應數據。

在這里插入圖片描述

技術實現

  • HTTP 接口:使用 RestTemplate、OkHttp、Apache HttpClient 等發起請求
  • RPC 接口:基于 RPC 框架提供的泛化調用能力構造請求

關鍵能力

  • 多線程并發執行,支持批量回放
  • 回放任務可手動觸發,也可通過策略定時運行
  • 支持失敗重試與限流策略,避免壓垮被測服務

五、差異對比

對比模塊將原始接口返回值與當前版本的回放結果進行對比,判斷是否存在行為變更。

對比方式

  • 文本對比(推薦):將返回結果轉為 JSON 結構,逐字段比對
  • 校驗和對比(不推薦):判斷整體一致性但缺乏定位能力

靈活配置

  • 支持忽略字段配置(如 UUID、traceId)
  • 支持字段級別容差設置(如時間戳誤差容忍 3s)

六、三種回放模式詳解

不同業務階段與環境下,可以靈活選擇回放模式:

1. 離線回放

僅調用新版本服務,使用歷史日志作為“期望值”。

在這里插入圖片描述

優點:不影響線上系統
缺點:若數據發生變化,對比結果失效


2. 實時回放(對比新舊服務)

手動觸發后,分別調用新舊版本,實時比較返回數據。

在這里插入圖片描述

優點:規避數據變更問題,結果更真實
缺點:兩次調用增加系統負載


3. 無錄制實時回放

完全實時處理:日志一進入消費隊列即觸發雙版本回放與對比。

在這里插入圖片描述

優點:最強覆蓋率,避免重要場景被采樣遺漏
缺點:性能壓力較大,需在資源允許下謹慎使用


七、使用注意事項與最佳實踐

  • 屏蔽寫接口:避免寫接口等副作用操作回放引發業務混亂
  • 合理限流:對線上環境回放要設限流閾值
  • 數據存儲生命周期:入參/出參數據定期清理,避免存儲崩潰
  • 差異字段管控:靈活配置忽略項,避免誤報
  • 自動告警機制:支持對比失敗數據告警與可視化管理

八、模擬核心Code

package com.example.autotest;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class AutoTestApplication {public static void main(String[] args) {SpringApplication.run(AutoTestApplication.class, args);}
}// 1. 日志收集 - Spring Interceptor
package com.example.autotest.interceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import com.example.autotest.mq.LogProducer;@Component
public class LoggingInterceptor implements HandlerInterceptor {@Autowiredprivate LogProducer logProducer;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 記錄請求時間、請求體等RequestContextHolder.start();return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {LogRecord record = new LogRecord();record.setAppName("order-service");record.setApi(request.getRequestURI());record.setRequestBody(RequestContextHolder.getRequestBody());record.setResponseBody(RequestContextHolder.getResponseBody());// 推送到 MQlogProducer.send(JSON.toJSONString(record));}
}// 2. MQ 生產者
package com.example.autotest.mq;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;@Component
public class LogProducer {@Autowiredprivate KafkaTemplate<String, String> kafkaTemplate;public void send(String message) {kafkaTemplate.send("auto-test-logs", message);}
}// 3. 日志消費 & 回放觸發
package com.example.autotest.consumer;import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.example.autotest.model.LogRecord;
import com.example.autotest.replay.ReplayService;@Service
public class LogConsumer {private final ReplayService replayService;public LogConsumer(ReplayService replayService) {this.replayService = replayService;}@KafkaListener(topics = "auto-test-logs")public void listen(String message) {LogRecord record = JSON.parseObject(message, LogRecord.class);// 異步觸發回放replayService.submit(record);}
}// 4. 回放服務
package com.example.autotest.replay;import com.example.autotest.model.LogRecord;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.alibaba.fastjson.JSON;@Service
public class ReplayService {private final ExecutorService executor = Executors.newFixedThreadPool(20);private final RestTemplate restTemplate = new RestTemplate();public void submit(LogRecord record) {executor.submit(() -> {// 調用舊版接口String oldRes = restTemplate.postForObject(record.getApi(), record.getRequestBody(), String.class);// 調用新版接口(假設前綴不同)String newApi = record.getApi().replace("/v1/", "/v2/");String newRes = restTemplate.postForObject(newApi, record.getRequestBody(), String.class);// 對比結果DiffResult diff = JsonDiffComparator.compare(record.getResponseBody(), newRes);if (!diff.isEqual()) {// 記錄差異或報警System.err.println("Data mismatch on API " + record.getApi() + ": " + diff.getDetails());}});}
}// 5. 差異對比工具
package com.example.autotest.replay;import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.flipkart.zjsonpatch.JsonDiff;public class JsonDiffComparator {private static final ObjectMapper mapper = new ObjectMapper();public static DiffResult compare(String expectedJson, String actualJson) {try {JsonNode e = mapper.readTree(expectedJson);JsonNode a = mapper.readTree(actualJson);JsonNode patch = JsonDiff.asJson(e, a);if (patch.size() == 0) {return new DiffResult(true, null);}return new DiffResult(false, patch.toString());} catch (Exception ex) {throw new RuntimeException(ex);}}
}// 6. 差異結果模型
package com.example.autotest.replay;public class DiffResult {private final boolean equal;private final String details;public DiffResult(boolean equal, String details) {this.equal = equal;this.details = details;}public boolean isEqual() { return equal; }public String getDetails() { return details; }
}

九、小結

通過日志收集、數據回放和差異對比三大模塊的組合,讀服務的測試回歸過程實現了自動化、精細化、可視化,徹底擺脫“人工全量測試回歸”的低效流程,極大地提升了系統重構與業務迭代的安全性與效率。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/904219.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/904219.shtml
英文地址,請注明出處:http://en.pswp.cn/news/904219.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于Spring Boot 3.0、ShardingSphere、PostgreSQL或達夢數據庫的分庫分表

要實現基于Spring Boot 3.0、ShardingSphere、PostgreSQL或達夢數據庫的分庫分表&#xff0c;首先需要對ShardingSphere進行一些基本配置。你提到的溯源碼、批次號等數據需要考慮到跨年數據的存儲&#xff0c;因此要設計一個能夠動態擴展的分表策略 添加ShardingSphere依賴 在…

位運算的應用

1. 判斷偶數&#xff0c;判斷最低位是0還是1即可&#xff0c;?求模快 x % 2 ! 0 //x正負都可以判斷&#xff1b;不?x%2 1&#xff0c;因為如果x為負奇數&#xff0c;x%2-1 (x & 0x1) 0 例如&#xff1a; int x; int main() { cin>>x; if((x & 0x1)0) cout<…

FOC算法開環控制基礎

1. 為什么要有FOC算法 先看看從有刷電機到無刷電機的簡單介紹&#xff0c;如下圖1&#xff0c;通電螺線圈會產生磁場&#xff0c;這個磁場會產生N級和S級&#xff0c;然后這個電磁鐵就可以吸引永磁體&#xff0c;S級吸引N級&#xff0c;N級吸引S級&#xff0c;通俗的來說&…

【計算機網絡】HTTP中GET和POST的區別是什么?

從以下幾個方面去說明&#xff1a; 1.定義 2.參數傳遞方式 3.安全性 4.冪等性 1.定義&#xff1a; GET&#xff1a; 獲取資源&#xff0c;通常請求數據而不改變服務器的狀態。POST&#xff1a; 提交數據到服務器&#xff0c;通常會改變服務器的狀態或副作用(如創建或更新資源…

7400MB/s5050TBW完美結合,全新希捷酷玩530R SSD體驗評測

7400MB/s&5050TBW完美結合&#xff0c;全新希捷酷玩530R SSD體驗評測 哈嘍小伙伴們好&#xff0c;我是Stark-C~ 說到希捷酷玩530 SSD&#xff0c;很多硬核進階玩家應該都知道&#xff0c;或者說正在使用&#xff08;比如說我~&#xff09;。 作為希捷大廠旗下高性能SSD的…

(undone) MIT6.S081 2023 學習筆記 (Day11: LAB10 mmap)

url: https://pdos.csail.mit.edu/6.1810/2023/labs/mmap.html mmap和munmap系統調用允許UNIX程序對其地址空間進行精細控制。它們可用于進程間共享內存、將文件映射到進程地址空間&#xff0c;并作為用戶級頁面錯誤處理方案的一部分&#xff0c;例如課程中討論的垃圾回收算法。…

Q_OBJECT宏的作用

Qt 中&#xff0c;如果一個類中定義了信號&#xff08;signals&#xff09;或槽&#xff08;slots&#xff09;&#xff0c;那么這個類必須包含 Q_OBJECT 宏。 Q_OBJECT宏是 Qt 元對象系統的核心部分&#xff0c;它使得信號和槽機制能夠正常工作。 Q_OBJECT宏是 Qt 的元對象系統…

信息安全基石:加解密技術的原理、應用與未來

信息加解密技術是信息安全領域的核心技術之一&#xff0c;以下為你詳細介紹&#xff1a; 一、加密技術 1.定義&#xff1a;加密是通過特定的算法和密鑰&#xff0c;將原始的明文信息轉化為看似無意義的密文信息的過程。這一過程使得信息在傳輸、存儲等過程中&#xff0c;即使…

LeetCode:返回倒數第k個結點

1、題目描述 實現一種算法&#xff0c;找出單向鏈表中倒數第 k 個節點。返回該節點的值。 注意&#xff1a;本題相對原題稍作改動 示例&#xff1a; 輸入&#xff1a; 1->2->3->4->5 和 k 2 輸出&#xff1a; 4 說明&#xff1a; 給定的 k 保證是有效的。 2、…

R004 -計算機硬件基礎

目錄 1.數據表示&計算機網絡組成 2.計算機網絡分類 3.馮諾依曼體系結構 4.指令系統基礎 5.指令系統類型 6.流水線技術 流水線周期 &#xff1a;各流水段中&#xff0c;執行時間最長的那一段。就是T 流水線時間&#xff1a;t 1t2t 3 (n-1) * T 7.流水線指標 8.存儲系…

Mybatis學習(下)

目錄 1. 動態sql的應用 1.2 1.2 1.3 、 、 標簽 1.4 1. 動態sql的應用 使用Mybatis框架時, 對于sql數據的操作量比較大的時候, 看著會覺得很亂, 可能寫著寫著就亂了, 或者說回過頭來發現sql語句寫錯了, 很麻煩, 所以動態sql就可以讓我們用Java代碼, 替換部分sql語句 1.2 &l…

iview 老版本合并單元格

新版的iview中已經支持了合并單元格了&#xff0c;我的版本比較老&#xff0c;為&#xff1a;"iview": "^3.5.2"。暫不支持。記錄一下別的大佬的方法。感覺思路比較活&#xff0c;正在這種思路需要在解決問題的過程中學習。 核心思路&#xff1a;通過rende…

FGMRES(Flexible Generalized Minimal Residual)方法

FGMRES&#xff08;Flexible Generalized Minimal Residual&#xff09;方法是GMRES的變種&#xff0c;主要用于處理變預處理子&#xff08;即每次迭代的預處理子可能不同&#xff09;的情況。與標準GMRES相比&#xff0c;FGMRES通過存儲預處理后的向量而非預處理子本身&#x…

自主采集高質量三維重建數據集指南:面向3DGS與NeRF的圖像與視頻拍攝技巧【2025最新版!!】

一、? 引言 隨著三維重建技術的飛速發展&#xff0c;NeRF&#xff08;Neural Radiance Fields&#xff09;與 3D Gaussian Splatting&#xff08;3DGS&#xff09;等方法成為重建真實場景和物體幾何細節的前沿方案。這些方法在大規模場景建模、機器人感知、文物數字化、工業檢…

HarmonyOS Next-DevEco Studio(5.0.2)無網絡環境配置(詳細教程)

開發者如果電腦處于完全無網環境&#xff0c;可以參考下面文檔進行相關配置 DevEco Studio(5.0.2)開發環境一覽&#xff1a; 工具版本DevEco Studio5.0.2openHarmonySDK14ohpm5.0.11node.js18.20.1hypium1.0.21 一、下載DevEco Studio&#xff08;5.0.2 Release&#xff09;…

MIT XV6 - 1.1 Lab: Xv6 and Unix utilities - sleep 是怎樣練成的?

接上文MIT XV6 - 1.1 Lab: Xv6 and Unix utilities - sleep 探究sleep.c是如何’煉成’的? 老實講&#xff0c;我不熟悉Makefile&#xff0c;最多寫過簡單的編譯和輔助腳本&#xff0c;拿到Xv6的Makefile是一臉懵的&#xff0c;至今還是一臉懵&#xff0c;那么我們上篇中新加的…

順序結構雙鏈表的實現

雙鏈表是用最快的時間實現鏈表的一種方式&#xff0c;具體的實現代碼如下&#xff1a; #pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h>typedef int LTDataType; typedef struct ListNode {LTDataType data;struct ListNode* next;/…

GoFrame 奉孝學習筆記

第一章節 GoFrame 是一款基礎設施建設比較完善的模塊化框架 GoFrame 是一款基礎設施建設比較完善的模塊化框架, Web Server 模塊是其中比較核心的模塊,我們這里將 Web 服務開發作為框架入門的選擇,便于大家更容易學習和理解。 用GOland編寫代碼 go.mod module goframePro…

pinia實現數據持久化插件pinia-plugin-persist-uni

在學習uniapp過程中&#xff0c;看到了pinia-plugin-persist-uni插件&#xff0c;以前面試過程中也有面試過說vuex數據刷新之前的數據就丟失了&#xff0c;之前回答的是把數據存儲到數據庫或者本地存儲。pinia-plugin-persist-uni本質上數據也是本地存儲。 1、安裝 npm instal…

Git 多賬號切換及全局用戶名設置不生效問,GIT進行上傳無權限問題

解決 Git 多賬號切換及全局用戶名設置不生效問題 在軟件開發過程中&#xff0c;我們經常會使用 Git 進行版本控制。有時&#xff0c;我們需要在同一臺機器上管理多個 Git 賬號&#xff0c;最近我在進行使用git的時候因為項目要進行上傳的不同的git賬號&#xff0c;但是通過本地…