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

SpringBoot+Redis實現(xiàn)外呼頻次限制功能的項目實踐

 更新時間:2025年07月07日 09:03:11   作者:sean_it  
本文提出基于SpringBoot和Redis的外呼頻次限制解決方案,采用Redis計數(shù)器與滑動窗口算法實現(xiàn)精準(zhǔn)控制,具有一定的參考價值,感興趣的可以了解一下

針對外呼場景中的號碼頻次限制需求(如每3天只能呼出1000通電話),我可以提供一個基于Spring Boot和Redis的完整解決方案。

方案設(shè)計

核心思路

  • 使用Redis的計數(shù)器+過期時間機制
  • 采用滑動窗口算法實現(xiàn)精確控制
  • 通過Lua腳本保證原子性操作

實現(xiàn)步驟

1. 添加依賴

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

2. 配置Redis

# application.yml
spring:
  redis:
    host: localhost
    port: 6379
    password: 
    database: 0

3. 實現(xiàn)頻次限制服務(wù)

@Service
public class CallFrequencyService {
    
    private final StringRedisTemplate redisTemplate;
    
    private static final String CALL_COUNT_PREFIX = "call:count:";
    private static final String CALL_TIMESTAMP_PREFIX = "call:timestamp:";
    
    @Autowired
    public CallFrequencyService(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    /**
     * 檢查并增加呼叫計數(shù)
     * @param callerNumber 主叫號碼
     * @param limit 限制次數(shù)
     * @param period 限制周期(秒)
     * @return 是否允許呼叫
     */
    public boolean checkAndIncrement(String callerNumber, int limit, long period) {
        String countKey = CALL_COUNT_PREFIX + callerNumber;
        String timestampKey = CALL_TIMESTAMP_PREFIX + callerNumber;
        
        // 使用Lua腳本保證原子性
        String luaScript = """
            local count = redis.call('get', KEYS[1])
            local timestamp = redis.call('get', KEYS[2])
            local now = tonumber(ARGV[3])
            
            if count and timestamp then
                if now - tonumber(timestamp) < tonumber(ARGV[2]) then
                    if tonumber(count) >= tonumber(ARGV[1]) then
                        return 0
                    else
                        redis.call('incr', KEYS[1])
                        return 1
                    end
                else
                    redis.call('set', KEYS[1], 1)
                    redis.call('set', KEYS[2], ARGV[3])
                    redis.call('expire', KEYS[1], ARGV[2])
                    redis.call('expire', KEYS[2], ARGV[2])
                    return 1
                end
            else
                redis.call('set', KEYS[1], 1)
                redis.call('set', KEYS[2], ARGV[3])
                redis.call('expire', KEYS[1], ARGV[2])
                redis.call('expire', KEYS[2], ARGV[2])
                return 1
            end
            """;
        
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Long.class);
        
        Long result = redisTemplate.execute(redisScript, 
            Arrays.asList(countKey, timestampKey),
            String.valueOf(limit), 
            String.valueOf(period),
            String.valueOf(System.currentTimeMillis() / 1000));
        
        return result != null && result == 1;
    }
    
    /**
     * 獲取剩余可呼叫次數(shù)
     * @param callerNumber 主叫號碼
     * @param limit 限制次數(shù)
     * @return 剩余次數(shù)
     */
    public int getRemainingCount(String callerNumber, int limit) {
        String countKey = CALL_COUNT_PREFIX + callerNumber;
        String countStr = redisTemplate.opsForValue().get(countKey);
        
        if (StringUtils.isBlank(countStr)) {
            return limit;
        }
        
        int used = Integer.parseInt(countStr);
        return Math.max(0, limit - used);
    }
}

4. 實現(xiàn)REST接口

@RestController
@RequestMapping("/api/call")
public class CallController {
    
    private static final int DEFAULT_LIMIT = 1000;
    private static final long DEFAULT_PERIOD = 3 * 24 * 60 * 60; // 3天(秒)
    
    @Autowired
    private CallFrequencyService callFrequencyService;
    
    @PostMapping("/check")
    public ResponseEntity<?> checkCallPermission(@RequestParam String callerNumber) {
        boolean allowed = callFrequencyService.checkAndIncrement(
            callerNumber, DEFAULT_LIMIT, DEFAULT_PERIOD);
        
        if (allowed) {
            return ResponseEntity.ok().body(Map.of(
                "allowed", true,
                "remaining", callFrequencyService.getRemainingCount(callerNumber, DEFAULT_LIMIT)
            ));
        } else {
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(Map.of(
                "allowed", false,
                "message", "呼叫次數(shù)超過限制"
            ));
        }
    }
    
    @GetMapping("/remaining")
    public ResponseEntity<?> getRemainingCount(@RequestParam String callerNumber) {
        int remaining = callFrequencyService.getRemainingCount(callerNumber, DEFAULT_LIMIT);
        return ResponseEntity.ok().body(Map.of(
            "remaining", remaining,
            "limit", DEFAULT_LIMIT
        ));
    }
}

5. 添加定時任務(wù)重置計數(shù)器(可選)

@Scheduled(cron = "0 0 0 * * ?") // 每天凌晨執(zhí)行
public void resetExpiredCounters() {
    // 可以定期清理過期的key,避免Redis積累太多無用key
    // 實際應(yīng)用中,依賴expire通常已經(jīng)足夠
}

方案優(yōu)化點

  • 分布式鎖:如果需要更精確的控制,可以在Lua腳本中加入分布式鎖
  • 多維度限制:可以擴展為基于號碼+時間段的多維度限制
  • 熔斷機制:當(dāng)達到限制閾值時,可以暫時熔斷該號碼的呼叫能力
  • 動態(tài)配置:將限制參數(shù)配置在數(shù)據(jù)庫或配置中心,實現(xiàn)動態(tài)調(diào)整

測試用例

@SpringBootTest
public class CallFrequencyServiceTest {
    
    @Autowired
    private CallFrequencyService callFrequencyService;
    
    @Test
    public void testCallFrequencyLimit() {
        String testNumber = "13800138000";
        int limit = 5;
        long period = 60; // 60秒
        
        // 前5次應(yīng)該成功
        for (int i = 0; i < limit; i++) {
            assertTrue(callFrequencyService.checkAndIncrement(testNumber, limit, period));
        }
        
        // 第6次應(yīng)該失敗
        assertFalse(callFrequencyService.checkAndIncrement(testNumber, limit, period));
        
        // 等待周期結(jié)束
        try {
            Thread.sleep(period * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // 新周期應(yīng)該重新計數(shù)
        assertTrue(callFrequencyService.checkAndIncrement(testNumber, limit, period));
    }
}

這個方案能夠高效、準(zhǔn)確地實現(xiàn)外呼頻次限制功能,通過Redis的高性能和原子性操作保證系統(tǒng)的可靠性,適合在生產(chǎn)環(huán)境中使用。

備注:

1、什么時間來統(tǒng)計使用次數(shù),真正呼叫出去才應(yīng)該是使用了呼叫次數(shù),所以需要異步在話單里來進行處理,且需要判斷話單的具體狀態(tài)是否認(rèn)為是這個號碼被使用了。

2、在獲取號碼階段只去判斷當(dāng)前的訪問次數(shù)是否超過了限制頻次即可,這樣的壞處時并不能精準(zhǔn)的去控制頻率(會有一小部分的時差),需要在性能和精確度上做綜合的權(quán)衡。

到此這篇關(guān)于SpringBoot+Redis實現(xiàn)外呼頻次限制功能的項目實踐的文章就介紹到這了,更多相關(guān)SpringBoot+Redis外呼頻次限制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot對接阿里云OSS的詳細(xì)步驟和流程

    SpringBoot對接阿里云OSS的詳細(xì)步驟和流程

    阿里云對象存儲服務(wù)(Object Storage Service,簡稱OSS)是一種海量、安全、低成本、高可靠的云存儲服務(wù),它適合存儲任意類型的文件,適用于海量數(shù)據(jù)存儲、圖片/視頻存儲、靜態(tài)網(wǎng)站托管等場景,本文給大家介紹了SpringBoot對接阿里云OSS的詳細(xì)步驟和流程
    2025-08-08
  • Java?List排序?qū)嵗a詳解

    Java?List排序?qū)嵗a詳解

    這篇文章主要介紹了Java?List排序的相關(guān)資料,Java排序方法包括自然排序、自定義排序、Lambda簡化及多條件排序,實現(xiàn)靈活且代碼簡潔,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-05-05
  • SpringBoot中間件ORM框架實現(xiàn)案例詳解(Mybatis)

    SpringBoot中間件ORM框架實現(xiàn)案例詳解(Mybatis)

    這篇文章主要介紹了SpringBoot中間件ORM框架實現(xiàn)案例詳解(Mybatis),本篇文章提煉出mybatis最經(jīng)典、最精簡、最核心的代碼設(shè)計,來實現(xiàn)一個mini-mybatis,從而熟悉并掌握ORM框架的涉及實現(xiàn),需要的朋友可以參考下
    2023-07-07
  • IDEA+GIT使用入門圖文詳解

    IDEA+GIT使用入門圖文詳解

    這篇文章主要介紹了IDEA+GIT使用入門詳解,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • idea啟動多個服務(wù)不顯示Services或者RunDashboard窗口的處理方法

    idea啟動多個服務(wù)不顯示Services或者RunDashboard窗口的處理方法

    這篇文章主要介紹了idea啟動多個服務(wù)不顯示Services或者RunDashboard窗口,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • 使用Apache?POI和SpringBoot實現(xiàn)Excel文件上傳和解析功能

    使用Apache?POI和SpringBoot實現(xiàn)Excel文件上傳和解析功能

    在現(xiàn)代企業(yè)應(yīng)用開發(fā)中,數(shù)據(jù)的導(dǎo)入和導(dǎo)出是一項常見且重要的功能需求,Excel?作為一種廣泛使用的電子表格工具,常常被用來存儲和展示數(shù)據(jù),下面我們來看看如何使用Apache?POI和SpringBoot實現(xiàn)Excel文件上傳和解析功能吧
    2025-01-01
  • Intellij IDEA導(dǎo)入eclipse web項目的操作步驟詳解

    Intellij IDEA導(dǎo)入eclipse web項目的操作步驟詳解

    Eclipse當(dāng)中的web項目都會有這兩個文件,但是idea當(dāng)中應(yīng)該是沒有的,所以導(dǎo)入會出現(xiàn)兼容問題,但是本篇文章會教大家如何導(dǎo)入,并且導(dǎo)入過后還能使用tomcat運行,需要的朋友可以參考下
    2023-08-08
  • Maven發(fā)布封裝到中央倉庫時候報錯:no default secret key

    Maven發(fā)布封裝到中央倉庫時候報錯:no default secret key

    這篇文章主要介紹了Maven發(fā)布封裝到中央倉庫時候報錯:no default secret key,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • Java調(diào)用打印機的2種方式舉例(無驅(qū)/有驅(qū))

    Java調(diào)用打印機的2種方式舉例(無驅(qū)/有驅(qū))

    我們平時使用某些軟件或者在超市購物的時候都會發(fā)現(xiàn)可以使用打印機進行打印,這篇文章主要給大家介紹了關(guān)于Java調(diào)用打印機的2種方式,分別是無驅(qū)/有驅(qū)的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • Java使用JSqlParser解析SQL語句應(yīng)用場景

    Java使用JSqlParser解析SQL語句應(yīng)用場景

    JSqlParser是一個功能全面的Java庫,用于解析SQL語句,支持多種SQL方言,它可以輕松集成到Java項目中,并提供靈活的操作方式,本文介紹Java使用JSqlParser解析SQL語句總結(jié),感興趣的朋友一起看看吧
    2024-09-09

最新評論

宁乡县| 丰台区| 井陉县| 公主岭市| 富顺县| 东明县| 绍兴县| 砚山县| 容城县| 太仓市| 囊谦县| 如东县| 姜堰市| 滨海县| 即墨市| 台北市| 长武县| 彰武县| 庆阳市| 灵石县| 贵港市| 加查县| 海丰县| 凌源市| 大城县| 尼玛县| 南充市| 集贤县| 曲周县| 柳州市| 平利县| 辽中县| 张北县| 淮阳县| 沁阳市| 东丰县| 化州市| 墨竹工卡县| 益阳市| 盖州市| 霞浦县|