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

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

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

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

1. 基于唯一標(biāo)識(shí)的去重機(jī)制

核心思想:為每次請(qǐng)求生成唯一標(biāo)識(shí)(如訂單號(hào)、請(qǐng)求 ID),服務(wù)端通過(guò)記錄該標(biāo)識(shí)是否已處理,避免重復(fù)執(zhí)行。

實(shí)現(xiàn)方式:

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

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

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

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

@Autowired
private StringRedisTemplate redisTemplate;

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

2. 令牌(Token)機(jī)制

核心思想:客戶端先向服務(wù)端申請(qǐng)令牌,請(qǐng)求接口時(shí)攜帶令牌,服務(wù)端驗(yàn)證令牌有效性后處理業(yè)務(wù),并標(biāo)記令牌為已使用。

實(shí)現(xiàn)流程:

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

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

3. 樂(lè)觀鎖機(jī)制

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

@Entity
public class Product {
    @Id
    private Long id;
    private Integer stock; // 庫(kù)存
    private Integer version; // 版本號(hào)
}

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

// 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)機(jī)控制

核心思想:通過(guò)狀態(tài)流轉(zhuǎn)約束,確保接口只能在特定狀態(tài)下執(zhí)行,避免重復(fù)操作(如訂單狀態(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("訂單已處理"); // 重復(fù)支付請(qǐng)求直接返回成功
    }
    
    // 執(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ā)場(chǎng)景)

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

// 基于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) {
            // 檢查是否已處理(雙重校驗(yàn))
            if (isProcessed(key)) {
                return Result.success("已處理");
            }
            // 執(zhí)行業(yè)務(wù)
            doBusiness();
            markAsProcessed(key); // 標(biāo)記為已處理
            return Result.success();
        } else {
            // 獲取鎖失敗,可能是重復(fù)請(qǐng)求
            return Result.fail("操作繁忙,請(qǐng)重試");
        }
    } finally {
        // 釋放鎖(僅釋放自己持有的鎖)
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

選擇建議

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

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

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

相關(guān)文章

  • Java ThreadLocal類(lèi)應(yīng)用實(shí)戰(zhàn)案例分析

    Java ThreadLocal類(lèi)應(yīng)用實(shí)戰(zhàn)案例分析

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

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

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

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

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

    Spring事務(wù)失效問(wèn)題分析及解決方案

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

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

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

    SpringBoot 代碼風(fēng)格推薦

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

    解決跨域請(qǐng)求,NG返回403(403并不一定是NG問(wèn)題)

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

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

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

    mybatis入門(mén)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

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

    SpringBoot配置攔截器的示例

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

最新評(píng)論

西吉县| 蒙自县| 寿光市| 武乡县| 宜丰县| 大丰市| 香河县| 澄城县| 庄浪县| 华亭县| 江油市| 前郭尔| 南昌市| 钟山县| 澄江县| 绩溪县| 江口县| 积石山| 沁水县| 岳西县| 民县| 垣曲县| 蓬安县| 齐河县| 贞丰县| 临泽县| 榆社县| 临清市| 灵武市| 镇原县| 田阳县| 许昌市| 南充市| 西峡县| 蓬溪县| 漳州市| 朝阳区| 绥中县| 冀州市| 灵山县| 金华市|