最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java中接口冪等性的五種實現(xiàn)方法

 更新時間:2025年09月28日 11:02:28   作者:forever銳  
在java中保證接口冪等性,即多次調(diào)用同一接口產(chǎn)生與單次調(diào)用相同的結(jié)果,不會引發(fā)副作用,需要結(jié)合業(yè)務場景選擇合適的方案,下面就來介紹五種實現(xiàn)方法,感興趣的可以了解一下

在 Java 中保證接口冪等性(即多次調(diào)用同一接口產(chǎn)生與單次調(diào)用相同的結(jié)果,不會引發(fā)副作用),需要結(jié)合業(yè)務場景選擇合適的方案。以下是常見的實現(xiàn)方式及技術(shù)細節(jié):

1. 基于唯一標識的去重機制

核心思想:為每次請求生成唯一標識(如訂單號、請求 ID),服務端通過記錄該標識是否已處理,避免重復執(zhí)行。

實現(xiàn)方式:

數(shù)據(jù)庫唯一約束:將唯一標識作為數(shù)據(jù)庫表的唯一索引,重復請求會觸發(fā)主鍵沖突異常,直接返回成功(或錯誤提示)。

// 示例:訂單表唯一索引(order_no)
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = "orderNo")})
public class Order {
    @Id
    private Long id;
    private String orderNo; // 唯一訂單號(作為冪等標識)
    // 其他字段...
}

// 服務層處理
@Transactional
public Result createOrder(OrderDTO dto) {
    try {
        // 嘗試插入訂單(依賴數(shù)據(jù)庫唯一約束)
        Order order = new Order();
        order.setOrderNo(dto.getOrderNo());
        orderRepository.save(order);
        // 執(zhí)行后續(xù)業(yè)務(如扣減庫存)
        return Result.success();
    } catch (DataIntegrityViolationException e) {
        // 唯一約束沖突,說明已處理過
        log.warn("訂單已存在: {}", dto.getOrderNo());
        return Result.success(); // 或返回已有結(jié)果
    }
}

緩存記錄(Redis) :利用 Redis 的SETNX(不存在則設置)特性,判斷請求是否已處理。

@Autowired
private StringRedisTemplate redisTemplate;

public Result processRequest(String requestId) {
    // 嘗試設置唯一標識,過期時間防止內(nèi)存溢出
    Boolean isFirst = redisTemplate.opsForValue().setIfAbsent(
        "idempotent:" + requestId, 
        "processed", 
        1, TimeUnit.HOURS
    );
    
    if (Boolean.TRUE.equals(isFirst)) {
        // 首次請求,執(zhí)行業(yè)務邏輯
        doBusiness();
        return Result.success();
    } else {
        // 重復請求,返回已有結(jié)果
        return Result.success("已處理");
    }
}

2. 令牌(Token)機制

核心思想:客戶端先向服務端申請令牌,請求接口時攜帶令牌,服務端驗證令牌有效性后處理業(yè)務,并標記令牌為已使用。

實現(xiàn)流程:

  1. 客戶端請求獲取令牌(服務端生成令牌并存儲到 Redis)。
  2. 客戶端攜帶令牌調(diào)用業(yè)務接口。
  3. 服務端校驗令牌:存在則處理業(yè)務并刪除令牌;不存在則拒絕。
// 生成令牌
public String generateToken() {
    String token = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set("token:" + token, "valid", 30, TimeUnit.MINUTES);
    return token;
}

// 校驗令牌并處理業(yè)務
public Result doBusiness(String token, BusinessDTO dto) {
    // 刪除令牌(原子操作,確保唯一處理)
    Boolean isValid = redisTemplate.delete("token:" + token);
    if (Boolean.TRUE.equals(isValid)) {
        // 令牌有效,執(zhí)行業(yè)務
        process(dto);
        return Result.success();
    } else {
        // 令牌無效(已使用或過期)
        return Result.fail("重復請求");
    }
}

3. 樂觀鎖機制

核心思想:適用于更新操作,通過版本號控制,確保只有版本匹配時才執(zhí)行更新,避免重復更新。

@Entity
public class Product {
    @Id
    private Long id;
    private Integer stock; // 庫存
    private Integer version; // 版本號
}

@Transactional
public Result reduceStock(Long productId, Integer quantity) {
    // 查詢商品及當前版本
    Product product = productRepository.findById(productId)
        .orElseThrow(() -> new RuntimeException("商品不存在"));
    
    // 檢查庫存
    if (product.getStock() < quantity) {
        return Result.fail("庫存不足");
    }
    
    // 樂觀鎖更新(where條件包含版本號)
    int rows = productRepository.reduceStock(
        productId, 
        quantity, 
        product.getVersion() // 當前版本
    );
    
    if (rows > 0) {
        return Result.success();
    } else {
        // 版本不匹配,說明已被其他請求處理
        return Result.fail("操作沖突,請重試");
    }
}

// Repository層SQL(JPA示例)
@Modifying
@Query("UPDATE Product p SET p.stock = p.stock - :quantity, p.version = p.version + 1 " +
       "WHERE p.id = :id AND p.version = :version")
int reduceStock(@Param("id") Long id, 
                @Param("quantity") Integer quantity, 
                @Param("version") Integer version);

4. 狀態(tài)機控制

核心思想:通過狀態(tài)流轉(zhuǎn)約束,確保接口只能在特定狀態(tài)下執(zhí)行,避免重復操作(如訂單狀態(tài)從 “待支付” 到 “已支付” 的單向流轉(zhuǎn))。

public enum OrderStatus {
    CREATED(1, "待支付"),
    PAID(2, "已支付"),
    CANCELLED(3, "已取消");
    
    private int code;
    private String desc;
    // 構(gòu)造器、getter...
}

@Transactional
public Result payOrder(Long orderId) {
    Order order = orderRepository.findById(orderId)
        .orElseThrow(() -> new RuntimeException("訂單不存在"));
    
    // 僅允許“待支付”狀態(tài)執(zhí)行支付
    if (order.getStatus() != OrderStatus.CREATED) {
        log.warn("訂單狀態(tài)異常: {}", orderId);
        return Result.success("訂單已處理"); // 重復支付請求直接返回成功
    }
    
    // 執(zhí)行支付邏輯(如調(diào)用支付網(wǎng)關(guān))
    boolean paySuccess = paymentGateway.pay(order);
    if (paySuccess) {
        order.setStatus(OrderStatus.PAID);
        orderRepository.save(order);
        return Result.success();
    } else {
        return Result.fail("支付失敗");
    }
}

5. 分布式鎖(高并發(fā)場景)

核心思想:在分布式系統(tǒng)中,通過分布式鎖(如 Redis、ZooKeeper)確保同一時間只有一個請求處理業(yè)務。

// 基于Redis的分布式鎖(使用Redisson)
@Autowired
private RedissonClient redissonClient;

public Result processDistributed(String key) {
    RLock lock = redissonClient.getLock("lock:" + key);
    try {
        // 嘗試獲取鎖,最多等待10秒,持有鎖1分鐘
        boolean locked = lock.tryLock(10, 60, TimeUnit.SECONDS);
        if (locked) {
            // 檢查是否已處理(雙重校驗)
            if (isProcessed(key)) {
                return Result.success("已處理");
            }
            // 執(zhí)行業(yè)務
            doBusiness();
            markAsProcessed(key); // 標記為已處理
            return Result.success();
        } else {
            // 獲取鎖失敗,可能是重復請求
            return Result.fail("操作繁忙,請重試");
        }
    } finally {
        // 釋放鎖(僅釋放自己持有的鎖)
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

選擇建議

  • 查詢接口:天然冪等,無需額外處理。
  • 新增操作:優(yōu)先使用 “唯一標識 + 數(shù)據(jù)庫 / Redis 去重”。
  • 更新操作:優(yōu)先使用 “樂觀鎖” 或 “狀態(tài)機”。
  • 分布式系統(tǒng):結(jié)合 “分布式鎖” 與 “唯一標識” 確保一致性。
  • 高并發(fā)場景:優(yōu)先使用 Redis(性能優(yōu)于數(shù)據(jù)庫)。

需注意:冪等性設計需結(jié)合業(yè)務場景,避免過度設計;同時要處理異常情況(如網(wǎng)絡超時),確??蛻舳酥卦嚂r的正確性。

到此這篇關(guān)于java中接口冪等性的五種實現(xiàn)方法的文章就介紹到這了,更多相關(guān)java 接口冪等性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java ThreadLocal類應用實戰(zhàn)案例分析

    Java ThreadLocal類應用實戰(zhàn)案例分析

    這篇文章主要介紹了Java ThreadLocal類應用,結(jié)合具體案例形式分析了java ThreadLocal類的功能、原理、用法及相關(guān)操作注意事項,需要的朋友可以參考下
    2019-09-09
  • Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之棧

    Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之棧

    棧(stack)又名堆棧,它是一種運算受限的線性表。限定僅在表尾進行插入和刪除操作的線性表。這一端被稱為棧頂,相對地,把另一端稱為棧底,棧是基礎(chǔ)中的基礎(chǔ),如果你還沒掌握透徹就來接著往下看吧
    2022-02-02
  • Java解析DICOM圖之如何獲得16進制數(shù)據(jù)詳解

    Java解析DICOM圖之如何獲得16進制數(shù)據(jù)詳解

    DICOM就是醫(yī)學數(shù)字成像和通信,是醫(yī)學圖像和相關(guān)信息的國際標準(ISO 12052),下面這篇文章主要給大家介紹了關(guān)于Java解析DICOM圖之如何獲得16進制數(shù)據(jù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下。
    2017-10-10
  • Spring事務失效問題分析及解決方案

    Spring事務失效問題分析及解決方案

    這篇文章主要介紹了Spring事務失效問題分析及解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01
  • 利用Intellij Idea連接遠程服務器實現(xiàn)遠程上傳部署功能

    利用Intellij Idea連接遠程服務器實現(xiàn)遠程上傳部署功能

    大家在使用Intellij Idea開發(fā)程序的時候,是不是需要部署到遠程SSH服務器運行呢,當然也可以直接在idea軟件內(nèi)容實現(xiàn)配置部署操作,接下來通過本文給大家分享利用Intellij Idea連接遠程服務器實現(xiàn)遠程上傳部署功能,感興趣的朋友跟隨小編一起看看吧
    2021-05-05
  • SpringBoot 代碼風格推薦

    SpringBoot 代碼風格推薦

    本文主要介紹了Spring Boot 項目定義了統(tǒng)一的包結(jié)構(gòu)與命名規(guī)范,涵蓋分層架構(gòu)、實體對象、工具類的命名與放置規(guī)則,強調(diào)各層對象隔離與技術(shù)層分,下面就來詳細的介紹一下
    2026-05-05
  • 解決跨域請求,NG返回403(403并不一定是NG問題)

    解決跨域請求,NG返回403(403并不一定是NG問題)

    這篇文章主要介紹了解決跨域請求,NG返回403(403并不一定是NG問題),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 修改request的parameter的幾種方式總結(jié)

    修改request的parameter的幾種方式總結(jié)

    這篇文章主要介紹了修改request的parameter的幾種方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • mybatis入門_動力節(jié)點Java學院整理

    mybatis入門_動力節(jié)點Java學院整理

    這篇文章主要為大家詳細介紹了mybatis入門的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • SpringBoot配置攔截器的示例

    SpringBoot配置攔截器的示例

    這篇文章主要介紹了SpringBoot配置攔截器的示例,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下
    2020-11-11

最新評論

菏泽市| 张家港市| 册亨县| 葫芦岛市| 万盛区| 思南县| 依兰县| 商洛市| 贵定县| 敦煌市| 岫岩| 吴川市| 沁源县| 会同县| 辉县市| 澜沧| 玉屏| 集安市| 敦煌市| 洛扎县| 凌源市| 富裕县| 永兴县| 沈阳市| 苍梧县| 高雄市| 宣武区| 偃师市| 张家口市| 隆回县| 芜湖市| 瑞昌市| 林口县| 富平县| 龙海市| 凌海市| 沁水县| 元阳县| 祁阳县| 普宁市| 乌苏市|