Redis接口訪問(wèn)優(yōu)化的方法步驟
說(shuō)明:之前寫過(guò)一篇使用Redis接口訪問(wèn)的博客,如下。最近有相關(guān)需求,把代碼拿出來(lái)后,做了一些優(yōu)化,挺有意思的,本文介紹在原基礎(chǔ)上
優(yōu)化
總的來(lái)說(shuō),這次使用Redis實(shí)現(xiàn)接口防抖,增加了一個(gè)時(shí)間段參數(shù),可以限制接口在某個(gè)時(shí)間段內(nèi),訪問(wèn)不能超過(guò)多少次。如下:
(自定義注解,打在接口上)
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定義注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LimitAccess { /** * 限制訪問(wèn)的key * @return */ String key(); /** * 限制訪問(wèn)次數(shù) * @return */ int times(); /** * 時(shí)間段 * @return */ int duration(); }
(切面,實(shí)現(xiàn)限制訪問(wèn))
import com.hezy.annotation.LimitAccess; import lombok.extern.log4j.Log4j2; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /** * AOP類(通知類) */ @Component @Aspect @Log4j2 public class LimitAspect { @Value("${access.enable:false}") private boolean enable; @Autowired private RedisTemplate redisTemplate; @Pointcut("@annotation(com.hezy.annotation.LimitAccess)") public void pt(){}; @Around("pt()") public Object aopAround(ProceedingJoinPoint pjp) throws Throwable { // 設(shè)置一個(gè)開關(guān),控制是否執(zhí)行 if (!enable) { return pjp.proceed(); } // 獲取切入點(diǎn)上面的自定義注解 Signature signature = pjp.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; // 獲取方法上面的注解 LimitAccess limitAccess = methodSignature.getMethod().getAnnotation(LimitAccess.class); // 獲取注解上面的屬性值 int limit = limitAccess.times(); String key = limitAccess.key(); int duration = limitAccess.duration(); // 遞增鍵的值,如果鍵不存在則初始化為1 Long currentCount = redisTemplate.opsForValue().increment(key, 1); // 如果鍵是新創(chuàng)建的,設(shè)置過(guò)期時(shí)間 if (currentCount != null && currentCount == 1) { redisTemplate.expire(key, duration, TimeUnit.SECONDS); } // 檢查是否超過(guò)限制 if (currentCount != null && currentCount > limit) { log.info("訪問(wèn)過(guò)于頻繁: " + pjp.toLongString()); throw new RuntimeException("訪問(wèn)過(guò)于頻繁"); } return pjp.proceed(); } }
(使用,在對(duì)應(yīng)的接口上,打上注解,填上數(shù)值,如下表示,1秒內(nèi)不能訪問(wèn)超過(guò)3次)
@LimitAccess(key = "test", times = 3, duration = 1) @GetMapping public String test() { return demoService.test(); }
另外,在代碼中加了一個(gè)開關(guān),可在配置文件中設(shè)置此配置,表示開啟或者關(guān)閉,默認(rèn)是關(guān)閉的
access: enable: true
啟動(dòng)項(xiàng)目,測(cè)試一下
思考
以上代碼,有兩點(diǎn)需要思考:
注解能不能加在Service層的方法上,加了有沒有用?另外加了會(huì)不會(huì)讓聲明式事務(wù)失效?
這個(gè)限制,沒有到用戶的維度,也就是說(shuō)所有的用戶,只要在一個(gè)時(shí)間段內(nèi)訪問(wèn)超過(guò)次數(shù)就限制,這顯然是不行的。有什么辦法嗎?
第一點(diǎn),我測(cè)試過(guò),注解可以加在Service層方法上,是可以的,不會(huì)讓事務(wù)失效,如下:
@LimitAccess(key = "deleteUserById", times = 3, duration = 1) @Transactional @Override public void deleteUserById(Integer id) { // 刪除用戶 userMapper.deleteUserById(id); int i = 1 / 0; // 刪除用戶對(duì)應(yīng)的角色 userMapper.deleteUserRoleMapper(id); }
接口限制,事務(wù),都生效了。
第二點(diǎn),這確實(shí)是個(gè)問(wèn)題,要做到針對(duì)用戶層面的接口限制是必須的,不然有一個(gè)用戶惡意刷新,其他用戶都用不了了,這怎么可以。要解決這個(gè)問(wèn)題,首先要拿到當(dāng)前操作用的標(biāo)識(shí),用戶名、用戶ID,然后再存入key的時(shí)候,拼接上這個(gè)標(biāo)識(shí)作為key。
String key = limitAccess.key() + 用戶標(biāo)識(shí)(用戶名、用戶id);
獲取當(dāng)前用戶信息,參考下面這篇文章:
如果你不是自己從0開發(fā)項(xiàng)目,一般成熟的項(xiàng)目都會(huì)有獲取當(dāng)前操作用戶信息的方式的。實(shí)在不行,你讓前端把用戶id作為參數(shù)傳給你,你在切面里獲取這個(gè)用戶id都可以。
總結(jié)
本文是對(duì)之前用Redis接口訪問(wèn)的優(yōu)化,以及對(duì)兩個(gè)問(wèn)題的考慮,希望能對(duì)大家有啟發(fā)。
獲取源碼:https://github.com/HeZhongYing/limit_access_demo
到此這篇關(guān)于Redis接口訪問(wèn)優(yōu)化的方法步驟的文章就介紹到這了,更多相關(guān)Redis接口訪問(wèn)優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis+自定義注解+AOP實(shí)現(xiàn)聲明式注解緩存查詢的示例
實(shí)際項(xiàng)目中,會(huì)遇到很多查詢數(shù)據(jù)的場(chǎng)景,這些數(shù)據(jù)更新頻率也不是很高,一般我們?cè)跇I(yè)務(wù)處理時(shí),會(huì)對(duì)這些數(shù)據(jù)進(jìn)行緩存,本文主要介紹了Redis+自定義注解+AOP實(shí)現(xiàn)聲明式注解緩存查詢的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04RedisTemplate 實(shí)現(xiàn)基于Value 操作的簡(jiǎn)易鎖機(jī)制(示例代碼)
本文將介紹如何使用 RedisTemplate 的 opsForValue().setIfAbsent() 方法來(lái)實(shí)現(xiàn)一種簡(jiǎn)單的鎖機(jī)制,并提供一個(gè)示例代碼,展示如何在 Java 應(yīng)用中利用這一機(jī)制來(lái)保護(hù)共享資源的訪問(wèn),感興趣的朋友跟隨小編一起看看吧2024-05-05Redis TTL命令實(shí)現(xiàn)數(shù)據(jù)生存時(shí)間
生存時(shí)間可以通過(guò)Redis中的不同命令來(lái)設(shè)置、查看和管理,TTL命令是其中之一,本文主要介紹了Redis TTL命令實(shí)現(xiàn)數(shù)據(jù)生存時(shí)間,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06利用Redis實(shí)現(xiàn)防止接口重復(fù)提交功能
大家好,本篇文章主要講的是利用Redis實(shí)現(xiàn)防止接口重復(fù)提交功能,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12Redis在項(xiàng)目中常見的12種使用場(chǎng)景示例和說(shuō)明
Redis是一個(gè)開源的高性能鍵值對(duì)數(shù)據(jù)庫(kù),它以其內(nèi)存中數(shù)據(jù)存儲(chǔ)、鍵過(guò)期策略、持久化、事務(wù)、豐富的數(shù)據(jù)類型支持以及原子操作等特性,在許多項(xiàng)目中扮演著關(guān)鍵角色,以下是整理的12個(gè)Redis在項(xiàng)目中常見的使用場(chǎng)景舉例說(shuō)明和解釋2024-06-06