<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
限流(Ratelimiting)指對應用服務的請求進行限制,例如某一介面的請求限制為 100 個每秒,對超過限制的請求則進行快速失敗或丟棄。
限流可以應對:
對於限流場景,一般需要考慮兩個維度的資訊:
時間限流基於某段時間範圍或者某個時間點,也就是我們常說的“時間視窗”,比如對每分鐘、每秒鐘的時間視窗做限定
資源基於可用資源的限制,比如設定最大存取次數,或最高可用連線數。
限流就是在某個時間視窗對資源存取做限制,比如設定每秒最多100個存取請求。
分散式限流相比於單機限流,只是把限流頻次分配到各個節點中,比如限制某個服務存取100qps,如果有10個節點,那麼每個節點理論上能夠平均被存取10次,如果超過了則進行頻率限制。
基於Guava的使用者端限流Guava是一個使用者端元件,在其多執行緒模組下提供了以RateLimiter為首的幾個限流支援類。它只能對“當前”服務進行限流,即它不屬於分散式限流的解決方案。
閘道器層限流服務閘道器,作為整個分散式鏈路中的第一道關卡,承接了所有使用者來訪請求。我們在閘道器層進行限流,就可以達到了整體限流的目的了。目前,主流的閘道器層有以軟體為代表的Nginx,還有Spring Cloud中的Gateway和Zuul這類閘道器層元件,也有以硬體為代表的F5。
中介軟體限流將限流資訊儲存在分散式環境中某個中介軟體裡(比如Redis快取),每個元件都可以從這裡獲取到當前時刻的流量統計,從而決定是拒絕服務還是放行流量。
限流元件目前也有一些開源元件提供了限流的功能,比如Sentinel就是一個不錯的選擇。Sentinel是阿里出品的開源元件,並且包含在了Spring Cloud Alibaba元件庫中。Hystrix也具有限流的功能。
Guava的Ratelimiter設計實現相當不錯,可惜只能支援單機,閘道器層限流如果是單機則不太滿足高可用,並且分散式閘道器的話還是需要依賴中介軟體限流,而redis之類的網路通訊需要佔用一小部分的網路消耗。阿里的Sentinel也是同理,底層使用的是redis或者zookeeper,每次存取都需要呼叫一次redis或者zk的介面。那麼在雲原生場景下,我們有沒有什麼更好的辦法呢?
對於極致追求高效能的服務不需要考慮熔斷、降級來說,是需要儘量減少網路之間的IO,那麼是否可以通過一個總限頻然後分配到具體的單機裡面去,在單機中實現平均的限流,比如限制某個ip的qps為100,服務總共有10個節點,那麼平均到每個服務裡就是10qps,此時就可以通過guava的ratelimiter來實現了,甚至說如果服務的節點動態調整,單個服務的qps也能動態調整。
在Spring Boot應用中,定義一個filter,獲取請求引數裡的key(ip、userId等),然後根據key來獲取rateLimiter,其中,rateLimiter的建立由資料庫定義的限頻數和副本數來判斷,最後,再通過rateLimiter.tryAcquire來判斷是否可以通過。
在實際的服務中,資料上報服務一般無法確定使用者端的上報時間、上報量,特別是對於這種要求高效能,服務一般都會用到HPA來實現動態擴縮容,所以,需要去間隔一段時間去獲取服務的副本數。
func CountDeploymentSize(namespace string, deploymentName string) *int32 { deployment, err := client.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{}) if err != nil { return nil } return deployment.Spec.Replicas }
用法:GET host/namespaces/test/deployments/k8s-rest-api直接即可。
在RateLimiterService中定義一個LoadingCache<String, RateLimiter>,其中,key可以為ip、userId等,並且,在多執行緒的情況下,使用refreshAfterWrite只阻塞載入資料的執行緒,其他執行緒則返回舊資料,極致發揮快取的作用。
private final LoadingCache<String, RateLimiter> loadingCache = Caffeine.newBuilder() .maximumSize(10_000) .refreshAfterWrite(20, TimeUnit.MINUTES) .build(this::createRateLimit); //定義一個預設最小的QPS private static final Integer minQpsLimit = 3000;
之後是建立rateLimiter,獲取總限頻數totalLimit和副本數replicas,之後是自己所需的邏輯判斷,可以根據totalLimit和replicas的情況來進行qps的限定。
public RateLimiter createRateLimit(String key) { log.info("createRateLimit,key:{}", key); int totalLimit = 獲取總限頻數,可以在資料庫中定義 Integer replicas = kubernetesService.getDeploymentReplicas(); RateLimiter rateLimiter; if (totalLimit > 0 && replicas == null) { rateLimiter = RateLimiter.create(totalLimit); } else if (totalLimit > 0) { int nodeQpsLimit = totalLimit / replicas; rateLimiter = RateLimiter.create(nodeQpsLimit > minQpsLimit ? nodeQpsLimit : minQpsLimit); } else { rateLimiter = RateLimiter.create(minQpsLimit); } log.info("create rateLimiter success,key:{},rateLimiter:{}", key, rateLimiter); return rateLimiter; }
根據key獲取RateLimiter,如果有特殊需求的話,需要判斷key不存在的嚐盡
public RateLimiter getRateLimiter(String key) { return loadingCache.get(key); }
最後一步,就是使用rateLimiter來進行限流,如果rateLimiter.tryAcquire()為true,則進行filterChain.doFilter(request, response),如果為false,則返回HttpStatus.TOO_MANY_REQUESTS
public class RateLimiterFilter implements Filter { @Resource private RateLimiterService rateLimiterService; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; String key = httpServletRequest.getHeader("key"); RateLimiter rateLimiter = rateLimiterService.getRateLimiter(key); if (rateLimiter != null) { if (rateLimiter.tryAcquire()) { filterChain.doFilter(request, response); } else { httpServletResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); } } else { filterChain.doFilter(request, response); } } }
為了方便對比效能之間的差距,我們在本地單機做了下列測試,其中,總限頻都設定為3萬。
其中,ping redis大概6-7ms左右,對應的,每次請求需要存取redis,時延都有大概6-7ms,效能下降明顯
效能幾乎追平無限流的場景,guava的rateLimiter確實表現卓越
在k8s中,服務是動態擴縮容的,相應的,每個節點應該都要有所變化,如果對外宣稱限頻100qps,而且後續業務方真的要求百分百準確,只能把LoadingCache<String, RateLimiter>的過期時間調小一點,讓它能夠近實時的更新單節點的qps。這裡還需要考慮一下k8s的壓力,因為每次都要獲取副本數,這裡也是需要做快取的
理論上是存在這個可能的,這個時候需要考慮一下初始的副本數的,擴縮容不能一蹴而就,一下子從1變為4變為幾十個這種。一般的話,生產環境肯定是不能只有一個節點,並且要考慮擴縮容的話,至於要有多個副本預備的
這個是依賴於k8s的service負載均衡策略的,這個我們之前做過實驗,流量確實是能夠均勻的落到節點上的。還有就是,我們整個限流都是基於k8s的,如果k8s出現問題,那就是整個叢集所有服務都有可能出現問題了。
到此這篇關於kubernetes實現分散式限流的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45