限流的目的是通過對(duì)并發(fā)訪問/請(qǐng)求進(jìn)行限速或者一個(gè)時(shí)間窗口內(nèi)的的請(qǐng)求進(jìn)行限速來保護(hù)系統(tǒng),一旦達(dá)到限制速率則可以拒絕服務(wù)。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、微信小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了滎經(jīng)免費(fèi)建站歡迎大家使用!
前幾天在DD的公眾號(hào),看了一篇關(guān)于使用 瓜娃 實(shí)現(xiàn)單應(yīng)用限流的方案 --》原文,參考《redis in action》 實(shí)現(xiàn)了一個(gè)jedis版本的,都屬于業(yè)務(wù)層次限制。 實(shí)際場景中常用的限流策略:
Nginx接入層限流
按照一定的規(guī)則如帳號(hào)、IP、系統(tǒng)調(diào)用邏輯等在Nginx層面做限流
業(yè)務(wù)應(yīng)用系統(tǒng)限流
通過業(yè)務(wù)代碼控制流量這個(gè)流量可以被稱為信號(hào)量,可以理解成是一種鎖,它可以限制一項(xiàng)資源最多能同時(shí)被多少進(jìn)程訪問。
代碼實(shí)現(xiàn)
import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; import redis.clients.jedis.ZParams; import java.util.List; import java.util.UUID; /** * @email wangiegie@gmail.com * @data 2017-08 */ public class RedisRateLimiter { private static final String BUCKET = "BUCKET"; private static final String BUCKET_COUNT = "BUCKET_COUNT"; private static final String BUCKET_MONITOR = "BUCKET_MONITOR"; static String acquireTokenFromBucket( Jedis jedis, int limit, long timeout) { String identifier = UUID.randomUUID().toString(); long now = System.currentTimeMillis(); Transaction transaction = jedis.multi(); //刪除信號(hào)量 transaction.zremrangeByScore(BUCKET_MONITOR.getBytes(), "-inf".getBytes(), String.valueOf(now - timeout).getBytes()); ZParams params = new ZParams(); params.weightsByDouble(1.0,0.0); transaction.zinterstore(BUCKET, params, BUCKET, BUCKET_MONITOR); //計(jì)數(shù)器自增 transaction.incr(BUCKET_COUNT); List<Object> results = transaction.exec(); long counter = (Long) results.get(results.size() - 1); transaction = jedis.multi(); transaction.zadd(BUCKET_MONITOR, now, identifier); transaction.zadd(BUCKET, counter, identifier); transaction.zrank(BUCKET, identifier); results = transaction.exec(); //獲取排名,判斷請(qǐng)求是否取得了信號(hào)量 long rank = (Long) results.get(results.size() - 1); if (rank < limit) { return identifier; } else {//沒有獲取到信號(hào)量,清理之前放入redis 中垃圾數(shù)據(jù) transaction = jedis.multi(); transaction.zrem(BUCKET_MONITOR, identifier); transaction.zrem(BUCKET, identifier); transaction.exec(); } return null; } }
調(diào)用
測(cè)試接口調(diào)用
@GetMapping("/") public void index(HttpServletResponse response) throws IOException { Jedis jedis = jedisPool.getResource(); String token = RedisRateLimiter.acquireTokenFromBucket(jedis, LIMIT, TIMEOUT); if (token == null) { response.sendError(500); }else{ //TODO 你的業(yè)務(wù)邏輯 } jedisPool.returnResource(jedis); }
優(yōu)化
使用攔截器 + 注解優(yōu)化代碼
攔截器
@Configuration static class WebMvcConfigurer extends WebMvcConfigurerAdapter { private Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class); @Autowired private JedisPool jedisPool; public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HandlerInterceptorAdapter() { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class); if (rateLimiter != null){ int limit = rateLimiter.limit(); int timeout = rateLimiter.timeout(); Jedis jedis = jedisPool.getResource(); String token = RedisRateLimiter.acquireTokenFromBucket(jedis, limit, timeout); if (token == null) { response.sendError(500); return false; } logger.debug("token -> {}",token); jedis.close(); } return true; } }).addPathPatterns("/*"); } }
定義注解
/** * @email wangiegie@gmail.com * @data 2017-08 * 限流注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RateLimiter { int limit() default 5; int timeout() default 1000; }
使用
@RateLimiter(limit = 2, timeout = 5000) @GetMapping("/test") public void test() { }
并發(fā)測(cè)試
工具:apache-jmeter-3.2
說明: 沒有獲取到信號(hào)量的接口返回500,status是紅色,獲取到信號(hào)量的接口返回200,status是綠色。
當(dāng)限制請(qǐng)求信號(hào)量為2,并發(fā)5個(gè)線程:
當(dāng)限制請(qǐng)求信號(hào)量為5,并發(fā)10個(gè)線程:
資料
基于reids + lua的實(shí)現(xiàn)
總結(jié)
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
當(dāng)前文章:基于Redis實(shí)現(xiàn)分布式應(yīng)用限流的方法
URL分享:http://jinyejixie.com/article34/iisjpe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、網(wǎng)站維護(hù)、響應(yīng)式網(wǎng)站、商城網(wǎng)站、手機(jī)網(wǎng)站建設(shè)、虛擬主機(jī)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)