亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

在分布式系統(tǒng)中,接口冪等性是一個(gè)非常重要的概念,它保證了在同樣的條件下,同一請求的多次執(zhí)行所產(chǎn)生的效果都是相同的。在實(shí)際開發(fā)中,為了防止重復(fù)提交或者重復(fù)操作帶來的問題,我們需要考慮如何實(shí)現(xiàn)接口冪等性。

下面我將介紹如何在 SpringBoot + MySQL + MyBatisPlus + Druid 的環(huán)境下實(shí)現(xiàn)接口冪等性。

  1. 什么是接口冪等性?

接口冪等性是指,對于相同的輸入,接口的輸出結(jié)果應(yīng)該相同。換句話說,如果接口已經(jīng)處理了一個(gè)請求并返回了結(jié)果,那么在相同的輸入條件下,該接口的后續(xù)請求應(yīng)該返回相同的結(jié)果,而不會產(chǎn)生任何新的副作用。

  1. 如何實(shí)現(xiàn)接口冪等性?

要實(shí)現(xiàn)接口冪等性,需要考慮以下幾個(gè)方面:

  • 請求唯一標(biāo)識:每個(gè)請求都應(yīng)該有一個(gè)唯一的標(biāo)識,可以是請求參數(shù)的組合或者是一個(gè)單獨(dú)的參數(shù)。
  • 冪等性校驗(yàn):每次請求到達(dá)服務(wù)器時(shí),服務(wù)器需要判斷該請求是否已經(jīng)被處理過,如果已經(jīng)被處理過,則直接返回處理結(jié)果,否則執(zhí)行請求操作,并記錄請求的唯一標(biāo)識,以便后續(xù)的冪等性校驗(yàn)。

在 SpringBoot + MySQL + MybatisPlus + Druid 的環(huán)境下,我們可以通過以下方式實(shí)現(xiàn)接口冪等性:

  • 在請求參數(shù)中添加一個(gè)冪等性校驗(yàn)碼(比如 UUID),用于唯一標(biāo)識每個(gè)請求。
  • 在請求處理前,先查詢冪等性校驗(yàn)碼是否已經(jīng)存在于數(shù)據(jù)庫中,如果存在則說明該請求已經(jīng)被處理過,直接返回結(jié)果。
  • 如果冪等性校驗(yàn)碼不存在于數(shù)據(jù)庫中,則執(zhí)行請求操作,并將冪等性校驗(yàn)碼插入到數(shù)據(jù)庫中。

下面是實(shí)現(xiàn)接口冪等性的示例代碼:

在請求參數(shù)中添加一個(gè)冪等性校驗(yàn)碼:

public class RequestDTO {
    private String idempotenceKey;
    // other request fields and methods
}

在 MybatisPlus 中創(chuàng)建對應(yīng)的實(shí)體類:

@Data
@TableName("idempotence_key")
public class IdempotenceKey {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String key;
    private Date createTime;
}

在 Controller 中實(shí)現(xiàn)冪等性校驗(yàn):

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @PostMApping("/user")
    public String createUser(@RequestBody RequestDTO request) {
        // 冪等性校驗(yàn)
        if (checkIdempotence(request.getIdempotenceKey())) {
            return "success";
        }
        // 執(zhí)行請求操作
        userService.createUser(request);
        // 插入冪等性校驗(yàn)碼
        saveIdempotence(request.getIdempotenceKey());
        return "success";
    }
}

在 Service 中實(shí)現(xiàn)冪等性校驗(yàn)和插入冪等性校驗(yàn)碼:

@Service
public class UserService {
    @Autowired
    private IdempotenceKeyMapper idempotenceKeyMapper;

    public void createUser(RequestDTO request) {
        // 創(chuàng)建用戶
        // ...
    }

    private boolean checkIdempotence(String key) {
        IdempotenceKey idempotenceKey = idempotenceKeyMapper.selectOne(new LambdaQueryWrapper<IdempotenceKey>().eq(IdempotenceKey::getKey, key));
        return idempotenceKey != null;
    }

    private void saveIdempotence(String key) {
        IdempotenceKey idempotenceKey = new IdempotenceKey();
        idempotenceKey.setKey(key);
        idempotenceKey.setCreateTime(new Date());
        idempotenceKeyMapper.insert(idempotenceKey);
    }
}

這里使用了 MybatisPlus 的 LambdaQueryWrapper 進(jìn)行查詢,并使用自動生成的 UUID 作為冪等性校驗(yàn)碼。

全局實(shí)現(xiàn)冪等性校驗(yàn)可以使用AOP(面向切面編程)來實(shí)現(xiàn),在方法執(zhí)行前先進(jìn)行冪等性校驗(yàn),如果已經(jīng)執(zhí)行過該方法,則直接返回結(jié)果??梢酝ㄟ^自定義注解來標(biāo)記需要進(jìn)行冪等性校驗(yàn)的方法。

以下是一個(gè)簡單的示例代碼:

  1. 自定義注解 Idempotent:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    long expireSeconds() default 60;
}
  1. 編寫 AOP 切面,用于攔截帶有 @Idempotent 注解的方法:
@Aspect
@Component
public class IdempotentAspect {
    @Autowired
    private IdempotenceKeyMapper idempotenceKeyMapper;

    @Pointcut("@annotation(com.example.demo.annotation.Idempotent)")
    public void idempotentPointcut() {}

    @Around("idempotentPointcut()")
    public Object idempotentAround(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Idempotent idempotent = method.getAnnotation(Idempotent.class);
        String key = getKey(point);

        if (StringUtils.isBlank(key)) {
            throw new RuntimeException("冪等性校驗(yàn)碼不能為空");
        }

        if (checkIdempotence(key)) {
            throw new RuntimeException("請勿重復(fù)操作");
        }

        saveIdempotence(key, idempotent.expireSeconds());
        return point.proceed();
    }

    private boolean checkIdempotence(String key) {
        IdempotenceKey idempotenceKey = idempotenceKeyMapper.selectOne(new LambdaQueryWrapper<IdempotenceKey>().eq(IdempotenceKey::getKey, key));
        return idempotenceKey != null;
    }

    private void saveIdempotence(String key, long expireSeconds) {
        IdempotenceKey idempotenceKey = new IdempotenceKey();
        idempotenceKey.setKey(key);
        idempotenceKey.setCreateTime(new Date());
        idempotenceKey.setExpireTime(new Date(System.currentTimeMillis() + expireSeconds * 1000));
        idempotenceKeyMapper.insert(idempotenceKey);
    }

    private String getKey(ProceedingJoinPoint point) {
        Object[] args = point.getArgs();
        if (args.length == 0) {
            return null;
        }
        return args[0].toString();
    }
}
  1. 在需要進(jìn)行冪等性校驗(yàn)的方法上添加 @Idempotent 注解:
 
@Service
public class UserService {
    @Autowired
    private IdempotenceKeyMapper idempotenceKeyMapper;

    @Idempotent(expireSeconds = 60)
    public void createUser(String username) {
        // 創(chuàng)建用戶
        // ...
    }
}

通過以上方式,在方法執(zhí)行前會先進(jìn)行冪等性校驗(yàn),如果已經(jīng)執(zhí)行過該方法,則直接返回結(jié)果,不會再次執(zhí)行。

在實(shí)際應(yīng)用中,需要考慮一些特殊情況的處理,以提高冪等性校驗(yàn)的準(zhǔn)確性和可靠性。下面列舉一些可能遇到的情況:

  1. 請求超時(shí)處理:由于冪等性校驗(yàn)碼是有過期時(shí)間的,如果客戶端發(fā)起的請求在冪等性校驗(yàn)碼過期后才到達(dá)服務(wù)器,那么該請求就不應(yīng)該再被視為重復(fù)請求。為了解決這個(gè)問題,可以在冪等性校驗(yàn)碼表中記錄請求的時(shí)間戳,并在校驗(yàn)冪等性校驗(yàn)碼時(shí)進(jìn)行時(shí)間戳比較,以判斷請求是否超時(shí)。

在冪等性校驗(yàn)碼表中添加一個(gè)請求時(shí)間戳的字段,將請求時(shí)間戳一并存儲,以便在校驗(yàn)冪等性校驗(yàn)碼時(shí)進(jìn)行時(shí)間戳比較。

CREATE TABLE `idempotent_key` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `key` varchar(128) NOT NULL COMMENT '冪等性校驗(yàn)碼',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  `expire_time` datetime NOT NULL COMMENT '過期時(shí)間',
  `request_time` datetime NOT NULL COMMENT '請求時(shí)間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_key` (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='冪等性校驗(yàn)碼表';

在進(jìn)行冪等性校驗(yàn)時(shí),需要先判斷冪等性校驗(yàn)碼是否過期,如果過期則不再進(jìn)行校驗(yàn)。

public void processRequest() {
    String key = generateIdempotentKey();
    LocalDateTime now = LocalDateTime.now();
    LocalDateTime expireTime = now.plusMinutes(5);
    LocalDateTime requestTime = now;

    // 將冪等性校驗(yàn)碼和請求時(shí)間戳存入數(shù)據(jù)庫中
    idempotentKeyDao.insert(key, expireTime, requestTime);

    // 判斷請求是否過期
    LocalDateTime threshold = now.minusMinutes(5);
    if (requestTime.isBefore(threshold)) {
        // 請求已經(jīng)過期,不再進(jìn)行冪等性校驗(yàn)
        return;
    }

    // 進(jìn)行冪等性校驗(yàn)
    boolean success = idempotentKeyDao.checkAndUpdate(key);
    if (!success) {
        // 冪等性校驗(yàn)失敗
        return;
    }

    // 執(zhí)行業(yè)務(wù)操作
    // ...
}
  1. 高并發(fā)下的冪等性校驗(yàn):在高并發(fā)場景下,多個(gè)請求可能同時(shí)到達(dá)服務(wù)器進(jìn)行冪等性校驗(yàn),這時(shí)需要保證校驗(yàn)的準(zhǔn)確性和唯一性。可以通過對冪等性校驗(yàn)碼進(jìn)行唯一索引的方式來保證每個(gè)冪等性校驗(yàn)碼只會出現(xiàn)一次,避免多個(gè)請求同時(shí)通過校驗(yàn)。

在冪等性校驗(yàn)碼表的 key 字段上添加唯一索引,以保證每個(gè)冪等性校驗(yàn)碼只會出現(xiàn)一次。

ALTER TABLE `idempotent_key` ADD UNIQUE INDEX `uk_key` (`key`);

在進(jìn)行冪等性校驗(yàn)時(shí),需要使用數(shù)據(jù)庫的唯一索引進(jìn)行校驗(yàn)。

public boolean checkAndUpdate(String key) {
    // 利用數(shù)據(jù)庫的唯一索引保證冪等性校驗(yàn)碼的唯一性
    int affectedRows = jdbcTemplate.update(
            "UPDATE idempotent_key SET request_count = request_count + 1 WHERE key = ?",
            key);
    return affectedRows == 1;
}
  1. 冪等性校驗(yàn)碼的重復(fù)利用:在一些場景下,比如一個(gè)請求執(zhí)行失敗需要重試,或者用戶進(jìn)行了一些撤銷操作后需要再次執(zhí)行該操作等,冪等性校驗(yàn)碼可能會被多次使用。為了避免重復(fù)利用同一個(gè)冪等性校驗(yàn)碼導(dǎo)致的校驗(yàn)失效,可以對冪等性校驗(yàn)碼進(jìn)行標(biāo)記,標(biāo)記該校驗(yàn)碼已被使用過,避免再次使用。

在冪等性校驗(yàn)碼表中添加一個(gè) used 字段,標(biāo)記該冪等性校驗(yàn)碼是否已被使用過。

在進(jìn)行冪等性校驗(yàn)時(shí),需要判斷該冪等性校驗(yàn)碼是否已經(jīng)被使用過,如果已經(jīng)被使用過,則不再進(jìn)行校驗(yàn)。

public boolean checkAndUpdate(String key) {
    // 判斷冪等性校驗(yàn)碼是否已經(jīng)被使用過
    boolean used = jdbcTemplate.queryForObject(
            "SELECT used FROM idempotent_key WHERE key = ?",
            Boolean.class,
            key);
    if (used) {
        // 冪等性校驗(yàn)碼已經(jīng)被使用過,不再進(jìn)行校驗(yàn)
        return true;
    }

    // 將冪等性校驗(yàn)碼標(biāo)記為已使用
    int affectedRows = jdbcTemplate.update(
            "UPDATE idempotent_key SET used = true WHERE key = ?",
            key);
    return affectedRows == 1;
}

冪等性校驗(yàn)碼的生成規(guī)則:冪等性校驗(yàn)碼的生成規(guī)則也需要考慮,應(yīng)該根據(jù)業(yè)務(wù)的特點(diǎn)來確定??梢圆捎秒S機(jī)數(shù)、UUID、請求參數(shù)哈希等方式生成冪等性校驗(yàn)碼。需要保證冪等性校驗(yàn)碼在相同的請求條件下生成的結(jié)果一致。

在分布式環(huán)境下,需要保證不同實(shí)例之間共享冪等性校驗(yàn)碼的狀態(tài)??梢允褂?redis 等分布式緩存來存儲冪等性校驗(yàn)碼狀態(tài)。

public boolean checkAndUpdate(String key) {
    // 從 Redis 中獲取冪等性校驗(yàn)碼的狀態(tài)
    boolean used = redisTemplate.opsForValue().get(key);
    if (used) {
        // 冪等性校驗(yàn)碼已經(jīng)被使用過,不再進(jìn)行校驗(yàn)
        return true;
    }

    // 將冪等性校驗(yàn)碼標(biāo)記為已使用
    redisTemplate.opsForValue().set(key, true);

    // 執(zhí)行業(yè)務(wù)操作
    // ...

    return true;
}

需要注意的是,由于 Redis 中存儲的數(shù)據(jù)可能會被意外刪除或過期,因此在使用 Redis 作為冪等性校驗(yàn)碼狀態(tài)存儲介質(zhì)時(shí),需要考慮數(shù)據(jù)丟失或過期的情況,確保系統(tǒng)的可靠性和正確性。

分享到:
標(biāo)簽:SpringBoot
用戶無頭像

網(wǎng)友整理

注冊時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定