欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用攔截器+Redis實現(xiàn)接口冪思路詳解

 更新時間:2023年08月19日 14:57:38   作者:逛窯子的李靖  
這篇文章主要介紹了使用攔截器+Redis實現(xiàn)接口冪等,接口冪等有很多種實現(xiàn)方式,攔截器/AOP+Redis,攔截器/AOP+本地緩存等等,本文講解一下通過攔截器+Redis實現(xiàn)冪等的方式,需要的朋友可以參考下

使用攔截器+Redis實現(xiàn)接口冪等

1.思路分析

接口冪等有很多種實現(xiàn)方式,攔截器/AOP+Redis,攔截器/AOP+本地緩存等等,本文講解一下通過攔截器+Redis實現(xiàn)冪等的方式。

其原理就是在攔截器中攔截請求,然后根據(jù)一個標(biāo)識符去redis中查詢是否已經(jīng)存在,如果存在,則說明當(dāng)前請求正在處理,拋出異常告訴前端請勿重復(fù)請求。

標(biāo)識符:一般可以使用token+methodType+uri作為標(biāo)識符,具體業(yè)務(wù)具體分析。

2.具體實現(xiàn)

2.1 創(chuàng)建redis工具類

import com.yunling.sys.config.exception.ParamValidateException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
 * Redis工具類
 *
 * @author 譚永強
 * @date 2023-08-15
 */
@Component
public class RedisUtils {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 寫入緩存
     *
     * @param key   建
     * @param value 值
     * @return 成功/失敗
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<String, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 寫入緩存設(shè)置時效時間
     *
     * @param key   鍵
     * @param value 值
     * @return 成功/失敗
     */
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<String, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 判斷緩存中是否有對應(yīng)的value
     *
     * @param key 鍵
     * @return 成功/失敗
     */
    public boolean exists(final String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
    /**
     * 讀取緩存
     *
     * @param key 鍵
     * @return 成功/失敗
     */
    public Object get(final String key) {
        return redisTemplate.opsForValue().get(key);
    }
    /**
     * 刪除對應(yīng)的value
     *
     * @param key 鍵
     * @return 成功/失敗
     */
    public boolean remove(final String key) {
        if (exists(key)) {
            return Boolean.TRUE.equals(redisTemplate.delete(key));
        }
        return false;
    }
    /**
     * 遞增
     *
     * @param key   鍵
     * @param delta 要增加幾(大于0)
     * @return 結(jié)果
     */
    public Long incr(String key, long delta) {
        if (ObjectUtils.isEmpty(key)) {
            throw new ParamValidateException("key值不能為空");
        }
        if (delta < 0) {
            throw new ParamValidateException("遞增因子必須大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }
    /**
     * 遞減
     *
     * @param key   鍵
     * @param delta 要減少幾(小于0)
     * @return 結(jié)果
     */
    public Long decr(String key, long delta) {
        if (ObjectUtils.isEmpty(key)) {
            throw new ParamValidateException("key值不能為空");
        }
        if (delta < 0) {
            throw new ParamValidateException("遞減因子必須大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }
}

2.2 自定義冪等注解

自定義冪等注解,將seconds設(shè)置為該注解的屬性,在攔截器中判斷方法上是否有該注解,如果有該注解,則說明當(dāng)前方法需要做冪等校驗。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 自動冪等
 * 該注解加在需要冪等的方法上,即可自動上線方法的冪等。
 *
 * @author 譚永強
 * @date 2023-08-15
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoIdempotent {
    /**
     * 限定時間(秒)
     * 限制多少秒內(nèi),每個用戶只能請求一次該接口。
     */
    long seconds() default 1;
}

2.3 自定義冪等攔截器

定義冪等接口用于攔截處理請求。

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.MD5Utils;
import com.yunling.sys.annotate.AutoIdempotent;
import com.yunling.sys.common.RedisUtils;
import com.yunling.sys.common.ResultData;
import com.yunling.sys.common.ReturnCode;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
 * 自動冪等攔截器
 *
 * @author 譚永強
 * @date 2023-08-15
 */
@Component
public class AutoIdempotentInterceptor extends HandlerInterceptorAdapter {
    @Resource
    private RedisUtils redisUtils;
    /**
     * @param request  請求
     * @param response 響應(yīng)
     * @param handler  處理
     * @return 結(jié)果
     * @throws Exception 異常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判斷請求是否為方法的請求
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod method = (HandlerMethod) handler;
        //獲取方法中是否有冪等性注解
        AutoIdempotent anno = method.getMethodAnnotation(AutoIdempotent.class);
        //若注解為空則直接返回
        if (Objects.isNull(anno)) {
            return true;
        }
        //限定時間
        long seconds = anno.seconds();
        //token
        String token = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (Objects.isNull(token)) {
            ResultData<String> resultData = ResultData.fail(ReturnCode.ACCESS_DENIED.getCode(), "token不能為空");
            write(response, JSON.toJSONString(resultData));
            return false;
        }
        //此處轉(zhuǎn)MD5的原因就是token長度太長了,轉(zhuǎn)成md5短一些,此操作并不是必須的
        String md5 = MD5Utils.md5Hex(token, StandardCharsets.UTF_8.toString());
        //使用token+method+uri作為key值,此處需要通過key值確定請求的唯一性,也可以使用其他的組合,只要保證唯一性即可
        String key = md5 + ":" + request.getMethod() + ":" + request.getRequestURI();
        Object requestCountObj = redisUtils.get(key);
        if (!ObjectUtils.isEmpty(requestCountObj)) {
            //不為空,說明不是第一次請求,直接報錯
            ResultData<String> resultData = ResultData.fail(ReturnCode.RC206.getCode(), "請求已提交,請勿重復(fù)請求");
            write(response, JSON.toJSONString(resultData));
            return false;
        }
        //若為空則為第一次請求
        return redisUtils.set(key, 1, seconds);
    }
    /**
     * 返回結(jié)果到前端
     *
     * @param response 響應(yīng)
     * @param body     結(jié)果
     * @throws IOException 異常
     */
    private void write(HttpServletResponse response, String body) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        ServletOutputStream os = response.getOutputStream();
        os.write(body.getBytes());
        os.flush();
        os.close();
    }
}

2.4 注入攔截器到容器

將攔截器注冊到容器中。

package com.yunling.sys.config;
import com.yunling.sys.config.interceptor.AutoIdempotentInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/**
 * 將攔截器注入到容器中
 *
 * @author 譚永強
 * @date 2023-08-15
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Resource
    private AutoIdempotentInterceptor autoIdempotentInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(autoIdempotentInterceptor);
    }
}

3.測試

@RestController
@RequestMapping("user")
public class SysUserController {
    /**
     * 用戶新增
     *
     * @param user 用戶信息
     */
    @AutoIdempotent(seconds = 60)
    @PostMapping("add")
    public void add(@RequestBody SysUser user) {
       //業(yè)務(wù)代碼.....
    }
}

請求該接口,如果在60s內(nèi)再次請求,就會返回重復(fù)請求的結(jié)果。seconds具體值設(shè)置多少由該接口的實際響應(yīng)時間為標(biāo)準(zhǔn),默認(rèn)值為1秒。

在這里插入圖片描述

到此這篇關(guān)于使用攔截器+Redis實現(xiàn)接口冪思路詳解的文章就介紹到這了,更多相關(guān)Redis接口冪內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis在計數(shù)器和人員記錄的事務(wù)操作應(yīng)用小結(jié)

    Redis在計數(shù)器和人員記錄的事務(wù)操作應(yīng)用小結(jié)

    Redis是一個高性能的鍵值存儲系統(tǒng),專于處理計數(shù)器和事務(wù)操作,它提供了INCR、DECR等命令來進行原子遞增或遞減操作,并通過MULTI、EXEC等命令實現(xiàn)事務(wù)操作,此外,Redis的Pipeline功能可減少網(wǎng)絡(luò)往返次數(shù),提高性能
    2024-10-10
  • Redis 設(shè)置密碼無效問題解決

    Redis 設(shè)置密碼無效問題解決

    本文主要介紹了Redis 設(shè)置密碼無效問題解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • 詳解Redis中key的命名規(guī)范和值的命名規(guī)范

    詳解Redis中key的命名規(guī)范和值的命名規(guī)范

    這篇文章主要介紹了詳解Redis中key的命名規(guī)范和值的命名規(guī)范,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • redis簡介_動力節(jié)點Java學(xué)院整理

    redis簡介_動力節(jié)點Java學(xué)院整理

    這篇文章主要介紹了redis簡介,Redis是一個開源的,先進的 key-value 存儲可用于構(gòu)建高性能,可擴展的 Web 應(yīng)用程序的解決方案,有興趣的可以了解一下
    2017-08-08
  • Redis 操作多個數(shù)據(jù)庫的配置的方法實現(xiàn)

    Redis 操作多個數(shù)據(jù)庫的配置的方法實現(xiàn)

    本文主要介紹了Redis 操作多個數(shù)據(jù)庫的配置的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Redis之Redisson原理詳解

    Redis之Redisson原理詳解

    Redisson 顧名思義,Redis 的兒子,本質(zhì)上還是 Redis 加鎖,不過是對 Redis 做了很多封裝,它不僅提供了一系列的分布式的 Java 常用對象,還提供了許多分布式服務(wù),本文將詳細給大家介紹Redisson原理
    2023-06-06
  • Redis并發(fā)訪問問題詳細講解

    Redis并發(fā)訪問問題詳細講解

    本文主要介紹了Redis如何應(yīng)對并發(fā)訪問,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-12-12
  • 基于Redis無序集合如何實現(xiàn)禁止多端登錄功能

    基于Redis無序集合如何實現(xiàn)禁止多端登錄功能

    這篇文章主要給你大家介紹了關(guān)于基于Redis無序集合如何實現(xiàn)禁止多端登錄功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-12-12
  • Redis遍歷海量數(shù)據(jù)集的幾種實現(xiàn)方法

    Redis遍歷海量數(shù)據(jù)集的幾種實現(xiàn)方法

    Redis作為一個高性能的鍵值存儲數(shù)據(jù)庫,廣泛應(yīng)用于各種場景,包括緩存、消息隊列、排行榜,本文主要介紹了Redis遍歷海量數(shù)據(jù)集的幾種實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • redis使用watch秒殺搶購實現(xiàn)思路

    redis使用watch秒殺搶購實現(xiàn)思路

    這篇文章主要為大家詳細介紹了redis使用watch秒殺搶購的實現(xiàn)思路,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-02-02

最新評論