http接口冪等性

????????實現 HTTP 接口的冪等性是確保多次相同請求產生相同結果的重要設計原則,尤其在網絡不穩定或分布式系統中非常關鍵。以下是幾種常見的實現方式:

1. 基于冪等性令牌(Token)的實現

適合支付、訂單創建等場景,步驟如下:

  1. 客戶端獲取令牌
  2. 客戶端攜帶令牌請求接口
  3. 服務端驗證并消費令牌
@Service
public class IdempotentService {// 實際項目中使用Redis等分布式緩存private final Set<String> tokenStore = ConcurrentHashMap.newKeySet();// 生成令牌public String generateToken() {String token = UUID.randomUUID().toString();tokenStore.add(token);return token;}// 驗證令牌public boolean validateToken(String token) {return tokenStore.remove(token); // 原子操作,確保唯一消費}
}@RestController
@RequestMapping("/orders")
public class OrderController {@Autowiredprivate IdempotentService idempotentService;@Autowiredprivate OrderService orderService;@PostMappingpublic ResponseEntity<?> createOrder(@RequestHeader("Idempotency-Token") String token,@RequestBody OrderRequest request) {// 驗證令牌if (!idempotentService.validateToken(token)) {return ResponseEntity.ok("重復請求,已處理");}// 處理訂單邏輯OrderResult result = orderService.createOrder(request);return ResponseEntity.ok(result);}
}

2. 基于數據庫唯一約束

通過數據庫唯一索引確保重復數據無法插入:

// 實體類
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"order_no"}) // 訂單號唯一約束
})
public class Order {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String orderNo;// 其他字段...
}// 服務層
@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactionalpublic Order createOrder(OrderRequest request) {String orderNo = generateOrderNo();Order order = new Order();order.setOrderNo(orderNo);// 設置其他字段...try {return orderRepository.save(order);} catch (DataIntegrityViolationException e) {// 捕獲唯一約束異常,視為重復請求log.warn("訂單已存在: {}", orderNo);return orderRepository.findByOrderNo(orderNo).orElseThrow();}}
}

3. 基于 Redis 的分布式鎖

適合分布式系統中的冪等性控制:

@Service
public class RedisIdempotentService {@Autowiredprivate StringRedisTemplate redisTemplate;private static final String IDEMPOTENT_KEY_PREFIX = "idempotent:";private static final long EXPIRATION_TIME = 30L; // 30秒過期public boolean checkAndLock(String key) {String redisKey = IDEMPOTENT_KEY_PREFIX + key;// SET NX 命令:不存在則設置,返回truereturn Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(redisKey, "1", EXPIRATION_TIME, TimeUnit.SECONDS));}public void releaseLock(String key) {String redisKey = IDEMPOTENT_KEY_PREFIX + key;redisTemplate.delete(redisKey);}
}// 控制器中使用
@PostMapping("/pay")
public ResponseEntity<?> pay(@RequestParam String orderId) {if (!redisIdempotentService.checkAndLock(orderId)) {return ResponseEntity.ok("支付請求已處理");}try {// 處理支付邏輯paymentService.processPayment(orderId);return ResponseEntity.ok("支付成功");} finally {redisIdempotentService.releaseLock(orderId);}
}

4. 基于 Spring AOP 的冪等性注解

通過自定義注解簡化冪等性控制:

// 自定義注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {// 冪等鍵的參數索引,默認取第一個參數int keyIndex() default 0;
}// AOP切面
@Aspect
@Component
public class IdempotentAspect {@Autowiredprivate RedisIdempotentService redisService;@Around("@annotation(idempotent) && args(.., request)")public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent, HttpServletRequest request) throws Throwable {// 獲取冪等鍵(此處從請求頭獲取)String key = request.getHeader("Idempotency-Key");if (StringUtils.isEmpty(key)) {return ResponseEntity.badRequest().body("缺少冪等鍵");}if (!redisService.checkAndLock(key)) {return ResponseEntity.ok("重復請求");}try {return joinPoint.proceed();} finally {// 非必須,根據業務設置過期時間自動釋放// redisService.releaseLock(key);}}
}// 接口使用
@PostMapping("/transfer")
@Idempotent
public ResponseEntity<?> transferMoney(@RequestBody TransferRequest request) {// 處理轉賬邏輯return ResponseEntity.ok(transferService.transfer(request));
}

總結

  1. 選擇合適的方案:查詢操作天然冪等,無需額外處理;寫操作根據業務選擇令牌或唯一約束
  2. 過期策略:冪等鍵需設置合理過期時間,避免存儲空間無限增長
  3. 異常處理:確保冪等控制邏輯的異常不影響正常業務流程
  4. 分布式場景:必須使用 Redis 等分布式存儲,避免單機存儲導致的問題
  5. 原子性保證:驗證和業務操作需在同一事務中,或使用分布式鎖確保原子性

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

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

相關文章

【華為OD】貪吃的猴子

文章目錄【華為OD】貪吃的猴子題目描述輸入描述輸出描述示例示例一示例二解題思路解法一&#xff1a;前綴和枚舉法Java實現Python實現C實現解法二&#xff1a;滑動窗口法Java實現Python實現C實現解法三&#xff1a;優化的動態規劃法Java實現Python實現C實現算法復雜度分析解法一…

Flie ,IO流(一)

一.File&#xff0c;IO流概述二.File文件1.File文件對象的創建&#xff08;路徑&#xff1a;&#xff09;2.常用方法1:判斷文件類型、獲取文件信息&#xff08;注意&#xff1a;&#xff09;3.常用方法2:創建文件、刪除文件&#xff08;creatNewFile&#xff08;&#xff09;會…

第2講 機器學習 - 導論

我們正處在一個"數據時代"&#xff0c;更強的計算能力和更豐富的存儲資源使數據總量與日俱增。然而真正的挑戰在于如何從海量數據中提取價值。企業與組織正通過數據科學、數據挖掘和機器學習的技術體系構建智能系統應對這一挑戰。其中&#xff0c;機器學習已成為計算…

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘python-dateutil’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘python-dateutil’問題 摘要 在日常 Python 開發過程中&#xff0c;我們經常會遇到各種 pip install 的報錯&#xff0c;尤其是在 PyCharm 2025 控制臺環境下&…

GitHub Pages 部署

地址&#xff1a;https://github.com/ 參考&#xff1a;https://blog.csdn.net/qq_45802269/article/details/127310952?ops_request_misc&request_id&biz_id102&utm_term%E5%9F%BA%E4%BA%8Egithub%E5%B9%B3%E5%8F%B0%EF%BC%8C%E5%8F%91%E5%B8%83vue%E9%A1%B9%E7%…

redis分布式鎖為什么采用Lua腳本實現。而不是事務

Redis 分布式鎖使用 Lua 腳本而非事務&#xff0c;核心原因是 Lua 腳本能保證分布式鎖操作的 “原子性” 和 “靈活性”&#xff0c;而 Redis 事務在某些場景下無法滿足分布式鎖的核心需求。一、Redis事務的局限性redis分布式鎖的核心是先判斷自己是否持有鎖&#xff0c;然后在…

Flutter之riverpod狀態管理Widget UI詳解

一、riverpod狀態管理中所涉及到的widget UI組件對比分析UI 組件狀態類型語法形式特點ConsumerWidget有狀態無狀態形式最常用&#xff0c;通過WidgetRef訪問provider&#xff0c;所謂無狀態&#xff0c;是指ConsumerWidegt不像StatefulWidegt那樣創建state,在它內部不可以定義狀…

什么是測試

文章目錄軟件測試是干什么的&#xff1f;軟件測試開發工程師是干什么的&#xff1f;測試工程師是干什么的&#xff1f;軟件測試開發工程師和測試工程師的區別效率工具能不能替代測試人員&#xff1f;測開人員的上手路線找工作/實習的時候怎么確定自己找的是測開還是測試呢&…

搭建分片集群

主從和哨兵可以解決高可用、高并發讀的問題。但是依然有兩個問題沒有解決&#xff1a;海量數據存儲問題高并發寫的問題使用分片集群可以解決上述問題&#xff0c;如圖:分片集群特征&#xff1a;集群中有多個master&#xff0c;每個master保存不同數據每個master都可以有多個sla…

在ubuntu系統中如何將docker安裝在指定目錄

在 Ubuntu 系統中&#xff0c;Docker 默認安裝路徑&#xff08;程序文件&#xff09;通常在/usr/bin等系統目錄&#xff0c;而核心數據&#xff08;鏡像、容器、卷等&#xff09;默認存儲在/var/lib/docker。若需將數據目錄指定到其他位置&#xff08;這是更常見的需求&#xf…

服務器都是用的iis, 前端部署后報跨域,不是用同一個服務器 是前端項目的服務器做Nginx轉發,還是后端項目的服務器做Nginx轉發?

當服務器環境為 IIS&#xff08;而非 Nginx&#xff09;&#xff0c;且前端、后端部署在不同服務器導致跨域時&#xff0c;核心思路與 Nginx 場景一致&#xff0c;但實現工具從「Nginx」替換為「IIS 配置」。此時依然存在 “后端服務器配置跨域頭” 和 “前端服務器配置反向代理…

【大前端】前端生成二維碼

前端生成二維碼有很多方法&#xff0c;常見的做法是使用 JavaScript 庫 來生成二維碼。下面整理幾種常用方案&#xff0c;并附示例代碼。1?? 使用 qrcode 庫&#xff08;推薦&#xff09;qrcode 是一個非常流行的前端 JS 庫&#xff0c;可以生成 Canvas 或者 SVG 的二維碼。安…

LeetCode 刷題【71. 簡化路徑】

71. 簡化路徑 自己做 解&#xff1a;遍歷檢查 class Solution { public:string simplifyPath(string path) {int p 0;string res;while(p < (int)path.size()){//情況1&#xff1a;遇到"/./" 》p跳過"/."if(p < (int)path.size() - 2 && p…

《算法闖關指南:優選算法-雙指針》--01移動零,02復寫零

&#x1f525;個人主頁&#xff1a;草莓熊Lotso &#x1f3ac;作者簡介&#xff1a;C研發方向學習者 &#x1f4d6;個人專欄&#xff1a;《C知識分享》《Linux 入門到實踐&#xff1a;零基礎也能懂》《數據結構與算法》《測試開發實戰指南》《算法題闖關指南》 ??人生格言&am…

【小白筆記】命令不對系統:無法將‘head’項識別為 cmdlet、函數、腳本文件或可運行程序的名稱

head : 無法將“head”項識別為 cmdlet、函數、腳本文件或可運行程序的名稱。請檢查名稱的拼寫&#xff0c;如果包括路徑&#xff0c;請確保路徑正確&#xff0c;然后再試一次。所在位置 行:1 字符: 1 head -5 train_data.csv ~~~~ CategoryInfo : ObjectNotFound: (h…

宋紅康 JVM 筆記 Day15|垃圾回收相關算法

一、今日視頻區間 P138-P153 二、一句話總結 標記階段&#xff1a;引用計數算法&#xff1b;標記階段&#xff1a;可達性分析算法&#xff1b;對象的finalization機制&#xff1b;MAT與JProfiler的GC Roots溯源&#xff1b;清除階段&#xff1a;標記-清除算法&#xff1b;清除階…

Go基礎(③Cobra)

Cobra 是幫你快速開發命令行工具的框架 假設你想做一個叫 todo 的命令行工具&#xff0c;實現這些功能&#xff1a; todo add "買牛奶" → 添加待辦 todo list → 查看所有待辦 todo done 1 → 標記第 1 個待辦為已完成 沒有 Cobra 的話&#xff0c;你需要自己寫代…

從 scheduler_tick 到上下文切換:深入解析 Linux 內核的 TIF_NEED_RESCHED 標志設置流程

Linux 是如何決定何時進行上下文切換的&#xff1f; 在Linux中&#xff0c;CPU 上下文切換是指當操作系統將 CPU 從一個進程切換到另一個進程時&#xff0c;保存當前進程的執行狀態&#xff0c;并加載新進程的執行狀態的過程就稱為上下文切換。 但在 Linux 內核中&#xff0c…

Redis 深度解析:數據結構、持久化與集群

Redis (Remote Dictionary Server) 是一種高性能的鍵值&#xff08;Key-Value&#xff09;內存數據庫&#xff0c;以其豐富的數據結構、極低的延遲、出色的穩定性和強大的集群能力&#xff0c;在現代應用程序的開發中扮演著至關重要的角色。無論是作為緩存、消息隊列、會話存儲…

HTTPS優化簡單總結

性能損耗選擇橢圓曲線&#xff0c;并生成橢圓曲線的計算耗時CA證書驗證的耗時計算pre-master的耗時硬件優化HTTPS是計算密集型任務&#xff0c;不是IO密集型任務所以硬件最好買更高級的CPU&#xff0c;而不是網卡&#xff0c;磁盤協議優化ECDHE代替RSA&#xff0c;因為ECDHE可以…