Artikel ini akan membawa anda memahami pengehadan semasa dalam Redis, dan memperkenalkan strategi pengehadan semasa yang mudah dan pengehadan arus corong saya harap ia akan membantu semua orang.
Cara mengatur rancangan apabila kapasiti pemprosesan sistem adalah Permintaan Luar yang terhad memberi tekanan kepada sistem. Mula-mula, mari kita lihat beberapa strategi pengehadan semasa yang mudah untuk mencegah serangan kekerasan. Sebagai contoh, jika anda ingin mengakses IP, anda hanya boleh mengaksesnya 10 kali setiap 5 saat, dan jika ia melebihi had, ia akan disekat. [Cadangan berkaitan: Tutorial video Redis]
Seperti yang ditunjukkan di atas, tetingkap gelongsor biasanya digunakan untuk mengira bilangan lawatan dalam selang waktu.
Gunakan zset
untuk merekodkan bilangan lawatan ke IP
Setiap IP
disimpan melalui key
score
menyimpan cap masa semasa value
secara unik menggunakan cap waktu atau UUID untuk melaksanakan
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 ? "限制访问" : "允许访问")); } } }
Hasil pelaksanaan
访问第1次, 结果:允许访问 访问第2次, 结果:允许访问 访问第3次, 结果:允许访问 访问第4次, 结果:允许访问 访问第5次, 结果:允许访问 访问第6次, 结果:限制访问 访问第7次, 结果:限制访问 ... ...
Kelemahan: Perlu merekodkan semua rekod tingkah laku dalam masa tetingkap, yang sangat besar Sebagai contoh, senario yang mengehadkan bilangan kali kepada tidak lebih daripada 1 juta kali dalam 60 saat tidak sesuai untuk had semasa sedemikian, kerana ia akan menggunakan banyak ruang storan.
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 ? "限制访问" : "允许访问")); } } }
Hasil pelaksanaan
访问第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
Beberapa medan objek, kami mendapati bahawa kami boleh menyimpan kandungan objek Funnel
ke dalam struktur hash
mengikut medan Apabila mengisi, keluarkan medan struktur hash
dan lakukan operasi logik, dan kemudian isi semula nilai baharu dalam struktur hash
, pengesanan kekerapan tingkah laku selesai. hash
, kemudian kendalikannya dalam memori, dan kemudian isi semula ke dalam struktur hash
Ketiga-tiga proses ini tidak boleh menjadi atom, yang bermaksud kawalan penguncian yang sesuai diperlukan. Setelah kunci dikunci, ini bermakna akan berlaku kegagalan kunci Jika kunci gagal, anda perlu memilih untuk mencuba semula atau menyerah. Redis-Cell
Penyelamat ada di sini! Redis 4.0 menyediakan modul Redis terhad semasa yang dipanggil redis-cell
. Modul ini juga menggunakan algoritma corong dan menyediakan arahan mengehadkan arus atom.
Modul ini hanya mempunyai satu arahan cl.throttle
, dan parameter serta nilai pulangannya agak rumit. Seterusnya, mari kita lihat cara menggunakan arahan ini.
> cl.throttle key:xxx 15 30 60 1
15
: 15 kapasiti Ini ialah kapasiti corong 30 60
: 30 operasi / 60 saat Ini ialah kadar kebocoran air 1
: memerlukan 1 kuota (parameter pilihan, nilai lalainya juga 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,单位秒)
Apabila melaksanakan perintah had semasa, jika ia ditolak, ia perlu dibuang atau dicuba semula. cl.throttle
Arahan ini sangat bernas, malah ia mengira masa mencuba semula untuk anda. Hanya dapatkan nilai keempat tatasusunan hasil yang dikembalikan dan laksanakan sleep
Jika anda tidak mahu menyekat benang, anda juga boleh menggunakan tugas berjadual tak segerak untuk mencuba semula.
Untuk lebih banyak pengetahuan berkaitan pengaturcaraan, sila lawati: Video Pengaturcaraan! !
Atas ialah kandungan terperinci Artikel untuk bercakap tentang strategi mengehadkan semasa di Redis. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!