java實現(xiàn)單機限流
何時使用限流:
比如你希望自己的應(yīng)用程序 QPS不要超過1000,那么RateLimiter設(shè)置1000的速率后,就會每秒往桶里 扔1000個令牌,RateLimiter經(jīng)常用于限制對一些物理資源或者邏輯資源的訪 問速率。
簡介:
對于單機版的限流,可以使用Google 開源的 Guava項目,這個項目提供了Google在Java項目中使用一些核心庫,包含集合(Collections),緩存(Caching),并發(fā)編程庫(Concurrency),常用注解(Common annotations),String操作,I/O操作方面的眾多非常實用的函數(shù)。
這個項目也包含了限流的功能,其原理是根據(jù)令牌桶算法來實現(xiàn)。
提供了兩種限流策略:
● 平滑突發(fā)限流(SmoothBursty)
● 平滑預熱限流(SmoothWarmingUp)實現(xiàn)。
依賴:
<dependency> ? <groupId>com.google.guava</groupId> ? <artifactId>guava</artifactId> ? <version>29.0-jre</version> </dependency>
方法描述:
模擬場景(示例):
場景一:
當我們希望某一個接口每秒的訪問量不超過10次
package org.xhs.test; import org.apache.curator.shaded.com.google.common.util.concurrent.RateLimiter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.*; /** ?* @Author: hu.chen ?* @Description: ?**/ public class Test { ? ? /** ? ? ?* 存儲接口名和令牌生成器的對應(yīng)關(guān)系 ? ? ?*/ ? ?private static Map<String, RateLimiter> interfaces = new ConcurrentHashMap<>(); ? ? ? ? /** ? ? ?* 線程池 ? ? ?*/ ? ? private static ExecutorService threadPool = new ThreadPoolExecutor(10,15,3,TimeUnit.SECONDS,new ArrayBlockingQueue<>(100)); ? ? public static void main(String[] args) throws InterruptedException { ? ? ? ? List<UserRequest> tasks = new ArrayList<UserRequest>(); ? ? ? ? // 準備工作,先初始化 10個線程(用戶),這10個用戶同時訪問一個接口 ? ? ? ? for (int i = 1; i <= 12; i++) { ? ? ? ? ? ? String ip = "127.0.0." + i; ? ? ? ? ? ? String userName="chenhu_"+i; ? ? ? ? ? ? String interfaceName="user/find_"; ? ? ? ? ? ? tasks.add(new UserRequest(ip,userName,interfaceName)); ? ? ? ? } ? ? ? ? // 先初始化好令牌生成器 ? ? ? ? for (UserRequest request : tasks) { ? ? ? ? ? ? // 根據(jù)接口名限流 ? ? ? ? ? ? RateLimiter rateLimiter = interfaces.get(request.getInterfaceName()); ? ? ? ? ? ? if(rateLimiter==null){ ? ? ? ? ? ? ? ? // 創(chuàng)建一個令牌生成器,每秒產(chǎn)生10個令牌 ? ? ? ? ? ? ? ? synchronized (interfaces) { ? ? ? ? ? ? ? ? ? ? if(rateLimiter==null) { ? ? ? ? ? ? ? ? ? ? ? ? rateLimiter = RateLimiter.create(10); ? ? ? ? ? ? ? ? ? ? ? ? // 將這個令牌生成器和具體的接口進行綁定 ? ? ? ? ? ? ? ? ? ? ? ? interfaces.put(request.getInterfaceName(),rateLimiter); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? // 休眠一秒,讓令牌生成器先生成令牌 ? ? ? ? Thread.sleep(1000); ? ? ? ? for (UserRequest request : tasks) { ? ? ? ? ? ? // 根據(jù)接口名限流 ? ? ? ? ? ? RateLimiter rateLimiter = interfaces.get(request.getInterfaceName()); ? ? ? ? ? ? // 獲取令牌桶中一個令牌,如果獲取不到,則等待 timeout 時間,如果還獲取不到,則返回false,反之則返回true ? ? ? ? ? ? // timeout設(shè)置為0,表示不等待 ? ? ? ? ? ? if(rateLimiter.tryAcquire(1,0,TimeUnit.SECONDS)){ ? ? ? ? ? ? ? ? // 得到令牌,處理請求 ? ? ? ? ? ? ? ? threadPool.execute(()->{ ? ? ? ? ? ? ? ? ? ? System.err.println("接口:"+request.getInterfaceName()+" 訪問還未達到上限,"+request.getUserName()+"可以訪問"); ? ? ? ? ? ? ? ? }); ? ? ? ? ? ? }else { ? ? ? ? ? ? ? ? // 已經(jīng)等待了10秒還獲取不到令牌,進行其他業(yè)務(wù)處理 ? ? ? ? ? ? ? ? System.err.println("當前時間訪問失敗,"+request.getUserName()+"無法獲取令牌"); ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? private static class UserRequest { ? ? ? ? /** ? ? ? ? ?* 請求用戶ip ? ? ? ? ?*/ ? ? ? ? private String ip; ? ? ? ? /** ? ? ? ? ?* 用戶名 ? ? ? ? ?*/ ? ? ? ? private String userName; ? ? ? ? /** ? ? ? ? ?* 請求的接口名 ? ? ? ? ?*/ ? ? ? ? private String interfaceName; ? ? ? ? public UserRequest(String ip, String userName, String interfaceName) { ? ? ? ? ? ? this.ip = ip; ? ? ? ? ? ? this.userName = userName; ? ? ? ? ? ? this.interfaceName = interfaceName; ? ? ? ? } ? ? ? ? public String getIp() {return ip;} ? ? ? ? public String getUserName() { return userName;} ? ? ? ? public String getInterfaceName() {return interfaceName;} ? ? } }
場景二:
當我們希望某一個用戶或者ip,每秒的訪問量不超過10
package org.xhs.test; import org.apache.curator.shaded.com.google.common.util.concurrent.RateLimiter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.*; /** ?* @Author: hu.chen ?* @Description: ?**/ public class Test { ? ? /** ? ? ?* 存儲用戶名和令牌生成器的對應(yīng)關(guān)系 ? ? ?*/ ? ?private static Map<String, RateLimiter> interfaces = new ConcurrentHashMap<>(); ? ? /** ? ? ?* 線程池 ? ? ?*/ ? ? private static ExecutorService threadPool = new ThreadPoolExecutor(10,15,3,TimeUnit.SECONDS,new ArrayBlockingQueue<>(100)); ? ? public static void main(String[] args) throws InterruptedException { ? ? ? ? List<UserRequest> tasks = new ArrayList<UserRequest>(); ? ? ? ? // 準備工作,先初始化 10個線程(用戶),這10個用戶同時訪問一個接口 ? ? ? ? for (int i = 1; i <= 12; i++) { ? ? ? ? ? ? String ip = "127.0.0." + i; ? ? ? ? ? ? String userName="chenhu_"; ? ? ? ? ? ? String interfaceName="user/find_"+i; ? ? ? ? ? ? tasks.add(new UserRequest(ip,userName,interfaceName)); ? ? ? ? } ? ? ? ? // 先初始化好令牌生成器 ? ? ? ? for (UserRequest request : tasks) { ? ? ? ? ? ? // 根據(jù)接口名限流 ? ? ? ? ? ? RateLimiter rateLimiter = interfaces.get(request.getUserName()); ? ? ? ? ? ? if(rateLimiter==null){ ? ? ? ? ? ? ? ? // 創(chuàng)建一個令牌生成器,每秒產(chǎn)生5個令牌 ? ? ? ? ? ? ? ? synchronized (interfaces) { ? ? ? ? ? ? ? ? ? ? if(rateLimiter==null) { ? ? ? ? ? ? ? ? ? ? ? ? rateLimiter = RateLimiter.create(10); ? ? ? ? ? ? ? ? ? ? ? ? // 將這個令牌生成器和具體的接口進行綁定 ? ? ? ? ? ? ? ? ? ? ? ? interfaces.put(request.getUserName(),rateLimiter); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? // 休眠一秒,讓令牌生成器先生成令牌 ? ? ? ? Thread.sleep(1000); ? ? ? ? for (UserRequest request : tasks) { ? ? ? ? ? ? // 根據(jù)接口名限流 ? ? ? ? ? ? RateLimiter rateLimiter = interfaces.get(request.getUserName()); ? ? ? ? ? ? // 獲取令牌桶中一個令牌,如果獲取不到,則等待 timeout 時間,如果還獲取不到,則返回false,反之則返回true ? ? ? ? ? ? // timeout設(shè)置為0,表示不等待 ? ? ? ? ? ? if(rateLimiter.tryAcquire(1,0,TimeUnit.SECONDS)){ ? ? ? ? ? ? ? ? // 得到令牌,處理請求 ? ? ? ? ? ? ? ? threadPool.execute(()->{ ? ? ? ? ? ? ? ? ? ? System.err.println("用戶:"+request.getUserName()+" 當前時間訪問次數(shù)還未達到上限,可以訪問"); ? ? ? ? ? ? ? ? }); ? ? ? ? ? ? }else { ? ? ? ? ? ? ? ? // 已經(jīng)等待了10秒還獲取不到令牌,進行其他業(yè)務(wù)處理 ? ? ? ? ? ? ? ? System.err.println("當前時間訪問失敗,"+request.getUserName()+"無法獲取令牌"); ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? private static class UserRequest { ? ? ? ? /** ? ? ? ? ?* 請求用戶ip ? ? ? ? ?*/ ? ? ? ? private String ip; ? ? ? ? /** ? ? ? ? ?* 用戶名 ? ? ? ? ?*/ ? ? ? ? private String userName; ? ? ? ? /** ? ? ? ? ?* 請求的接口名 ? ? ? ? ?*/ ? ? ? ? private String interfaceName; ? ? ? ? public UserRequest(String ip, String userName, String interfaceName) { ? ? ? ? ? ? this.ip = ip; ? ? ? ? ? ? this.userName = userName; ? ? ? ? ? ? this.interfaceName = interfaceName; ? ? ? ? } ? ? ? ? public String getIp() {return ip;} ? ? ? ? public String getUserName() { return userName;} ? ? ? ? public String getInterfaceName() {return interfaceName;} ? ? } }
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java8中用foreach循環(huán)獲取對象的index下標詳解
這篇文章主要給大家介紹了關(guān)于Java8中用foreach循環(huán)獲取對象的index下標的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04MybatisPlus查詢條件為空字符串或null問題及解決
這篇文章主要介紹了MybatisPlus查詢條件為空字符串或null問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06Springboot整合JPA配置多數(shù)據(jù)源流程詳解
這篇文章主要介紹了Springboot整合JPA配置多數(shù)據(jù)源,JPA可以通過實體類生成數(shù)據(jù)庫的表,同時自帶很多增刪改查方法,大部分sql語句不需要我們自己寫,配置完成后直接調(diào)用方法即可,很方便2022-11-11