Redis의 현재 제한 전략에 대해 설명하는 기사
이 기사에서는 Redis의 전류 제한을 이해하고 간단한 전류 제한 전략과 퍼널 전류 제한을 소개합니다. 이 글이 모든 사람에게 도움이 되기를 바랍니다.
1. 단순 전류 제한
기본 원칙
시스템 처리 용량이 제한될 때 계획되지 않은 요청을 정리하여 시스템에 압력을 가하는 방법. 먼저, 무차별 대입 공격을 방지하기 위한 몇 가지 간단한 전류 제한 전략을 살펴보겠습니다. 예를 들어, 특정 IP에 접근을 원할 경우 5초에 10번만 접근이 가능하며, 한도를 초과하면 차단됩니다. [관련 추천 : Redis 영상 튜토리얼]
위에서 보듯이 슬라이딩 윈도우는 일반적으로 간격 내 방문 횟수를 계산하는 데 사용됩니다.
zset
를 사용하여 IP
방문 횟수를 기록하세요. 각 IP
는 key
와 score 현재 타임스탬프를 저장합니다. <code>값
은 타임스탬프 또는 UUIDzset
记录 IP
访问次数,每个 IP
通过 key
保存下来,score
保存当前时间戳,value
唯一用时间戳或者UUID来实现
代码实现
public class RedisLimiterTest { private Jedis jedis; public RedisLimiterTest(Jedis jedis) { this.jedis = jedis; } /** * @param ipAddress Ip地址 * @param period 特定的时间内,单位秒 * @param maxCount 最大允许的次数 * @return */ public boolean isIpLimit(String ipAddress, int period, int maxCount) { String key = String.format("ip:%s", ipAddress); // 毫秒时间戳 long currentTimeMillis = System.currentTimeMillis(); Pipeline pipe = jedis.pipelined(); // redis事务,保证原子性 pipe.multi(); // 存放数据,value 和 score 都使用毫秒时间戳 pipe.zadd(key, currentTimeMillis, "" + UUID.randomUUID()); // 移除窗口区间所有的元素 pipe.zremrangeByScore(key, 0, currentTimeMillis - period * 1000); // 获取时间窗口内的行为数量 Response<Long> count = pipe.zcard(key); // 设置 zset 过期时间,避免冷用户持续占用内存,这里宽限1s pipe.expire(key, period + 1); // 提交事务 pipe.exec(); pipe.close(); // 比较数量是否超标 return count.get() > maxCount; } public static void main(String[] args) { Jedis jedis = new Jedis("localhost", 6379); RedisLimiterTest limiter = new RedisLimiterTest(jedis); for (int i = 1; i <= 20; i++) { // 验证IP 10秒钟之内只能访问5次 boolean isLimit = limiter.isIpLimit("222.73.55.22", 10, 5); System.out.println("访问第" + i + "次, 结果:" + (isLimit ? "限制访问" : "允许访问")); } } }
执行结果
访问第1次, 结果:允许访问 访问第2次, 结果:允许访问 访问第3次, 结果:允许访问 访问第4次, 结果:允许访问 访问第5次, 结果:允许访问 访问第6次, 结果:限制访问 访问第7次, 结果:限制访问 ... ...
缺点:要记录时间窗口所有的行为记录,量很大,比如,限定60s内不能超过100万次这种场景,不太适合这样限流,因为会消耗大量的储存空间。
二、漏斗限流
基本原理
- 漏斗的容量是限定的,如果满了,就装不进去了。
- 如果将漏嘴放开,水就会往下流,流走一部分之后,就又可以继续往里面灌水。
- 如果漏嘴流水的速率大于灌水的速率,那么漏斗永远都装不满。
- 如果漏嘴流水速率小于灌水的速率,那么一旦漏斗满了,灌水就需要暂停并等待漏斗腾空。
示例代码
public class FunnelLimiterTest { static class Funnel { int capacity; // 漏斗容量 float leakingRate; // 漏嘴流水速率 int leftQuota; // 漏斗剩余空间 long leakingTs; // 上一次漏水时间 public Funnel(int capacity, float leakingRate) { this.capacity = capacity; this.leakingRate = leakingRate; this.leftQuota = capacity; this.leakingTs = System.currentTimeMillis(); } void makeSpace() { long nowTs = System.currentTimeMillis(); long deltaTs = nowTs - leakingTs; // 距离上一次漏水过去了多久 int deltaQuota = (int) (deltaTs * leakingRate); // 腾出的空间 = 时间*漏水速率 if (deltaQuota < 0) { // 间隔时间太长,整数数字过大溢出 this.leftQuota = capacity; this.leakingTs = nowTs; return; } if (deltaQuota < 1) { // 腾出空间太小 就等下次,最小单位是1 return; } this.leftQuota += deltaQuota; // 漏斗剩余空间 = 漏斗剩余空间 + 腾出的空间 this.leakingTs = nowTs; if (this.leftQuota > this.capacity) { // 剩余空间不得高于容量 this.leftQuota = this.capacity; } } boolean watering(int quota) { makeSpace(); if (this.leftQuota >= quota) { // 判断剩余空间是否足够 this.leftQuota -= quota; return true; } return false; } } // 所有的漏斗 private Map<String, Funnel> funnels = new HashMap<>(); /** * @param capacity 漏斗容量 * @param leakingRate 漏嘴流水速率 quota/s */ public boolean isIpLimit(String ipAddress, int capacity, float leakingRate) { String key = String.format("ip:%s", ipAddress); Funnel funnel = funnels.get(key); if (funnel == null) { funnel = new Funnel(capacity, leakingRate); funnels.put(key, funnel); } return !funnel.watering(1); // 需要1个quota } public static void main(String[] args) throws Exception{ FunnelLimiterTest limiter = new FunnelLimiterTest(); for (int i = 1; i <= 50; i++) { // 每1s执行一次 Thread.sleep(1000); // 漏斗容量是2 ,漏嘴流水速率是0.5每秒, boolean isLimit = limiter.isIpLimit("222.73.55.22", 2, (float)0.5/1000); System.out.println("访问第" + i + "次, 结果:" + (isLimit ? "限制访问" : "允许访问")); } } }
执行结果
访问第1次, 结果:允许访问 # 第1次,容量剩余2,执行后1 访问第2次, 结果:允许访问 # 第2次,容量剩余1,执行后0 访问第3次, 结果:允许访问 # 第3次,由于过了2s, 漏斗流水剩余1个空间,所以容量剩余1,执行后0 访问第4次, 结果:限制访问 # 第4次,过了1s, 剩余空间小于1, 容量剩余0 访问第5次, 结果:允许访问 # 第5次,由于过了2s, 漏斗流水剩余1个空间,所以容量剩余1,执行后0 访问第6次, 结果:限制访问 # 以此类推... 访问第7次, 结果:允许访问 访问第8次, 结果:限制访问 访问第9次, 结果:允许访问 访问第10次, 结果:限制访问
- 我们观察
Funnel
对象的几个字段,我们发现可以将Funnel
对象的内容按字段存储到一个hash
结构中,灌水的时候将hash
结构的字段取出来进行逻辑运算后,再将新值回填到hash
结构中就完成了一次行为频度的检测。 - 但是有个问题,我们无法保证整个过程的原子性。从
hash
结构中取值,然后在内存里运算,再回填到hash
结构,这三个过程无法原子化,意味着需要进行适当的加锁控制。而一旦加锁,就意味着会有加锁失败,加锁失败就需要选择重试或者放弃。 - 如果重试的话,就会导致性能下降。如果放弃的话,就会影响用户体验。同时,代码的复杂度也跟着升高很多。这真是个艰难的选择,我们该如何解决这个问题呢?
Redis-Cell
救星来了!
Redis-Cell
Redis 4.0 提供了一个限流 Redis 模块,它叫 redis-cell
cl.throttle
,它的参数和返回值都略显复杂,接下来让我们来看看这个指令具体该如何使用。> cl.throttle key:xxx 15 30 60 1
로그인 후 복사15
: 15 capacity 这是漏斗容量30 60
: 30 operations / 60 seconds 这是漏水速率1
: need 1 quota (可选参数,默认值也是1)
> cl.throttle laoqian:reply 15 30 60
1) (integer) 0 # 0 表示允许,1表示拒绝
2) (integer) 15 # 漏斗容量capacity
3) (integer) 14 # 漏斗剩余空间left_quota
4) (integer) -1 # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒)
5) (integer) 2 # 多长时间后,漏斗完全空出来(left_quota==capacity,单位秒)
로그인 후 복사
> cl.throttle key:xxx 15 30 60 1
15
: 15 capacity 这是漏斗容量30 60
: 30 operations / 60 seconds 这是漏水速率1
: need 1 quota (可选参数,默认值也是1)> cl.throttle laoqian:reply 15 30 60 1) (integer) 0 # 0 表示允许,1表示拒绝 2) (integer) 15 # 漏斗容量capacity 3) (integer) 14 # 漏斗剩余空间left_quota 4) (integer) -1 # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒) 5) (integer) 2 # 多长时间后,漏斗完全空出来(left_quota==capacity,单位秒)
在执行限流指令时,如果被拒绝了,就需要丢弃或重试。cl.throttle
指令考虑的非常周到,连重试时间都帮你算好了,直接取返回结果数组的第四个值进行 sleep
코드 구현
2. 퍼널 전류 제한 🎜🎜기본 원칙🎜🎜
- 깔때기는 제한되어 있어 가득 차면 들어갈 수 없습니다.
- 주둥이를 놓으면 물이 아래로 흘러 일부가 흘러나간 후 계속 물을 채울 수 있습니다.
- 주둥이에서 나오는 물의 유속이 물을 채우는 속도보다 크면 깔때기가 가득 차지 않습니다.
- 깔때기의 유속이 충전 속도보다 낮으면 깔때기가 가득 차면 충전을 일시 중지하고 깔때기가 비워질 때까지 기다려야 합니다.
🎜샘플 코드🎜🎜rrreee🎜🎜실행 결과🎜🎜rrreee
-
Funnel
을 관찰합니다 object 여러 필드에서 Funnel
개체의 콘텐츠가 필드별로 hash
구조에 저장될 수 있음을 발견했습니다. 논리 연산을 위해 필드를 가져온 후 새 값이 해시
구조에 백필되어 동작 빈도 감지가 완료됩니다.
- 그러나 문제가 있습니다. 전체 프로세스의 원자성을 보장할 수 없다는 점입니다.
hash
구조에서 값을 가져온 다음 이를 메모리에서 작동시킨 다음 hash
구조에 다시 채웁니다. 이 세 가지 프로세스는 원자적일 수 없습니다. 즉, 적절한 잠금이 가능합니다. 통제가 필요합니다. 잠금이 잠기면 잠금이 실패한다는 의미입니다. 잠금이 실패하면 다시 시도하거나 포기해야 합니다.
- 다시 시도하면 성능이 저하됩니다. 포기하면 사용자 경험에 영향을 미칩니다. 동시에 코드의 복잡성도 많이 증가했습니다. 이것은 정말 어려운 선택입니다. 이 문제를 어떻게 해결합니까?
Redis-Cell
구세주가 여기 있습니다!
🎜Redis-Cell🎜🎜🎜Redis 4.0은 redis-cell
이라는 현재 제한된 Redis 모듈을 제공합니다. 이 모듈은 또한 깔대기 알고리즘을 사용하고 원자 전류 제한 지침을 제공합니다.
이 모듈에는 명령 cl.throttle
이 하나만 있고 해당 매개변수와 반환 값이 약간 복잡합니다. 다음으로 이 명령을 사용하는 방법을 살펴보겠습니다. 🎜rrreee
-
15
: 15 용량 이게 깔때기 용량이에요
-
30 60
: 30회 작업 / 60초 이게 물이에요 누출률
-
1
: 1개의 할당량이 필요합니다(선택적 매개변수, 기본값도 1입니다)
rrreee🎜현재 제한 명령을 실행할 때, 거부되었습니다. 삭제하거나 다시 시도해야 합니다. cl.throttle
명령은 매우 사려 깊고 재시도 시간까지 계산해 줍니다. 반환된 결과 배열의 네 번째 값을 가져와서 sleep
을 수행하세요. 스레드를 차단하고 싶지 않다면 비동기 예약 작업으로 다시 시도할 수도 있습니다. 🎜🎜더 많은 프로그래밍 관련 지식을 보려면 🎜프로그래밍 비디오🎜를 방문하세요! ! 🎜
- 깔때기는 제한되어 있어 가득 차면 들어갈 수 없습니다.
- 주둥이를 놓으면 물이 아래로 흘러 일부가 흘러나간 후 계속 물을 채울 수 있습니다.
- 주둥이에서 나오는 물의 유속이 물을 채우는 속도보다 크면 깔때기가 가득 차지 않습니다.
- 깔때기의 유속이 충전 속도보다 낮으면 깔때기가 가득 차면 충전을 일시 중지하고 깔때기가 비워질 때까지 기다려야 합니다.
🎜샘플 코드🎜🎜rrreee🎜🎜실행 결과🎜🎜rrreee
-
Funnel
을 관찰합니다 object 여러 필드에서 Funnel
개체의 콘텐츠가 필드별로 hash
구조에 저장될 수 있음을 발견했습니다. 논리 연산을 위해 필드를 가져온 후 새 값이 해시
구조에 백필되어 동작 빈도 감지가 완료됩니다.
- 그러나 문제가 있습니다. 전체 프로세스의 원자성을 보장할 수 없다는 점입니다.
hash
구조에서 값을 가져온 다음 이를 메모리에서 작동시킨 다음 hash
구조에 다시 채웁니다. 이 세 가지 프로세스는 원자적일 수 없습니다. 즉, 적절한 잠금이 가능합니다. 통제가 필요합니다. 잠금이 잠기면 잠금이 실패한다는 의미입니다. 잠금이 실패하면 다시 시도하거나 포기해야 합니다.
- 다시 시도하면 성능이 저하됩니다. 포기하면 사용자 경험에 영향을 미칩니다. 동시에 코드의 복잡성도 많이 증가했습니다. 이것은 정말 어려운 선택입니다. 이 문제를 어떻게 해결합니까?
Redis-Cell
구세주가 여기 있습니다!
🎜Redis-Cell🎜🎜🎜Redis 4.0은 redis-cell
이라는 현재 제한된 Redis 모듈을 제공합니다. 이 모듈은 또한 깔대기 알고리즘을 사용하고 원자 전류 제한 지침을 제공합니다.
이 모듈에는 명령 cl.throttle
이 하나만 있고 해당 매개변수와 반환 값이 약간 복잡합니다. 다음으로 이 명령을 사용하는 방법을 살펴보겠습니다. 🎜rrreee
-
15
: 15 용량 이게 깔때기 용량이에요
-
30 60
: 30회 작업 / 60초 이게 물이에요 누출률
-
1
: 1개의 할당량이 필요합니다(선택적 매개변수, 기본값도 1입니다)
rrreee🎜현재 제한 명령을 실행할 때, 거부되었습니다. 삭제하거나 다시 시도해야 합니다. cl.throttle
명령은 매우 사려 깊고 재시도 시간까지 계산해 줍니다. 반환된 결과 배열의 네 번째 값을 가져와서 sleep
을 수행하세요. 스레드를 차단하고 싶지 않다면 비동기 예약 작업으로 다시 시도할 수도 있습니다. 🎜🎜더 많은 프로그래밍 관련 지식을 보려면 🎜프로그래밍 비디오🎜를 방문하세요! ! 🎜
Funnel
을 관찰합니다 object 여러 필드에서 Funnel
개체의 콘텐츠가 필드별로 hash
구조에 저장될 수 있음을 발견했습니다. 논리 연산을 위해 필드를 가져온 후 새 값이 해시
구조에 백필되어 동작 빈도 감지가 완료됩니다. hash
구조에서 값을 가져온 다음 이를 메모리에서 작동시킨 다음 hash
구조에 다시 채웁니다. 이 세 가지 프로세스는 원자적일 수 없습니다. 즉, 적절한 잠금이 가능합니다. 통제가 필요합니다. 잠금이 잠기면 잠금이 실패한다는 의미입니다. 잠금이 실패하면 다시 시도하거나 포기해야 합니다. Redis-Cell
구세주가 여기 있습니다! redis-cell
이라는 현재 제한된 Redis 모듈을 제공합니다. 이 모듈은 또한 깔대기 알고리즘을 사용하고 원자 전류 제한 지침을 제공합니다.
이 모듈에는 명령 cl.throttle
이 하나만 있고 해당 매개변수와 반환 값이 약간 복잡합니다. 다음으로 이 명령을 사용하는 방법을 살펴보겠습니다. 🎜rrreee-
15
: 15 용량 이게 깔때기 용량이에요 -
30 60
: 30회 작업 / 60초 이게 물이에요 누출률 -
1
: 1개의 할당량이 필요합니다(선택적 매개변수, 기본값도 1입니다)
cl.throttle
명령은 매우 사려 깊고 재시도 시간까지 계산해 줍니다. 반환된 결과 배열의 네 번째 값을 가져와서 sleep
을 수행하세요. 스레드를 차단하고 싶지 않다면 비동기 예약 작업으로 다시 시도할 수도 있습니다. 🎜🎜더 많은 프로그래밍 관련 지식을 보려면 🎜프로그래밍 비디오🎜를 방문하세요! ! 🎜위 내용은 Redis의 현재 제한 전략에 대해 설명하는 기사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











1. [시작] 메뉴를 시작하여 [cmd]를 입력하고 [명령 프롬프트]를 마우스 오른쪽 버튼으로 클릭한 후 [관리자 권한으로 실행]을 선택합니다. 2. 다음 명령을 순서대로 입력합니다(주의 깊게 복사하여 붙여넣기): SCconfigwuauservstart=auto, Enter SCconfigbitsstart=auto, Enter 누르기 SCconfigcryptsvcstart=auto, Enter SCconfigtrustedinstallerstart=auto, Enter SCconfigwuauservtype=share, Enter netstopwuauserv , Enter netstopcryptS 누르기

PHP 함수 병목 현상은 성능 저하로 이어지며, 이는 병목 현상 기능을 찾아 성능 분석 도구를 사용하는 단계를 통해 해결할 수 있습니다. 재계산을 줄이기 위해 결과를 캐싱합니다. 작업을 병렬로 처리하여 실행 효율성을 높입니다. 문자열 연결을 최적화하고 대신 내장 함수를 사용하세요. 사용자 정의 함수 대신 내장 함수를 사용하십시오.

GolangAPI의 캐싱 전략은 성능을 향상시키고 서버 부하를 줄일 수 있습니다. 일반적으로 사용되는 전략은 LRU, LFU, FIFO 및 TTL입니다. 최적화 기술에는 적절한 캐시 스토리지 선택, 계층적 캐싱, 무효화 관리, 모니터링 및 조정이 포함됩니다. 실제 사례에서 LRU 캐시는 데이터베이스에서 사용자 정보를 얻기 위한 API를 최적화하는 데 사용됩니다. 그렇지 않으면 캐시를 데이터베이스에서 얻은 후 업데이트할 수 있습니다.

Erlang과 Go 사이에는 성능 차이가 있습니다. Erlang은 동시성이 뛰어나고 Go는 더 높은 처리량과 더 빠른 네트워크 성능을 제공합니다. Erlang은 높은 동시성을 요구하는 시스템에 적합한 반면, Go는 높은 처리량과 짧은 대기 시간을 요구하는 시스템에 적합합니다.

PHP 개발에서 캐싱 메커니즘은 자주 액세스하는 데이터를 메모리나 디스크에 임시 저장하여 데이터베이스 액세스 횟수를 줄여 성능을 향상시킵니다. 캐시 유형에는 주로 메모리, 파일 및 데이터베이스 캐시가 포함됩니다. 캐싱은 내장 함수나 캐시_get() 및 Memcache와 같은 타사 라이브러리를 사용하여 PHP에서 구현할 수 있습니다. 일반적인 실제 응용 프로그램에는 쿼리 성능을 최적화하기 위한 데이터베이스 쿼리 결과 캐싱과 렌더링 속도를 높이기 위한 페이지 출력 캐싱이 포함됩니다. 캐싱 메커니즘은 웹사이트 응답 속도를 효과적으로 향상시키고, 사용자 경험을 향상시키며, 서버 부하를 줄입니다.

Redis 캐시를 사용하면 PHP 배열 페이징 성능을 크게 최적화할 수 있습니다. 이는 다음 단계를 통해 달성할 수 있습니다. Redis 클라이언트를 설치합니다. Redis 서버에 연결합니다. 캐시 데이터를 생성하고 "page:{page_number}" 키를 사용하여 각 데이터 페이지를 Redis 해시에 저장합니다. 캐시에서 데이터를 가져오고 대규모 어레이에서 비용이 많이 드는 작업을 피하세요.

먼저 시스템 언어를 중국어 간체 표시로 설정하고 다시 시작해야 합니다. 물론 이전에 표시 언어를 중국어 간체로 변경했다면 이 단계를 건너뛰어도 됩니다. 다음으로 레지스트리 조작을 시작하여 regedit.exe를 실행하고 왼쪽 탐색바 또는 상단 주소 표시줄의 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlNlsLanguage로 직접 이동한 후 InstallLanguage 키 값과 Default 키 값을 0804로 수정합니다(영어 en-로 변경하려는 경우). 먼저 시스템 표시 언어를 en-us로 설정하고 시스템을 다시 시작한 다음 모든 항목을 0409로 변경해야 합니다. 이 시점에서 시스템을 다시 시작해야 합니다.

네, Navicat은 사용자가 키를 관리하고, 값을 보고, 명령을 실행하고, 활동을 모니터링하고, 문제를 진단할 수 있는 Redis에 연결할 수 있습니다. Redis에 연결하려면 Navicat에서 "Redis" 연결 유형을 선택하고 서버 세부 정보를 입력하세요.
