日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

单机和分布式场景下,有哪些流控方案?

發布時間:2024/9/3 编程问答 66 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单机和分布式场景下,有哪些流控方案? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
簡介:不同的場景下所需的流控算法不盡相同,那應該如何選擇適用的流控方案呢?本文分享單機及分布式流控場景下,簡單窗口、滑動窗口、漏桶、令牌桶、滑動日志等幾種流控算法的思路和代碼實現,并總結了各自的復雜度和適用場景。較長,同學們可收藏后再看。

一 流控的場景

流控的意義其實無需多言了。最常用的場景下,流控是為了保護下游有限的資源不被流量沖垮,保證服務的可用性,一般允許流控的閾值有一定的彈性,偶爾的超量訪問是可以接受的。

有的時候,流控服務于收費模式,比如某些云廠商會對調用 API 的頻次進行計費。既然涉及到錢,一般就不允許有超出閾值的調用量。

這些不同的場景下,適用的流控算法不盡相同。大多數情況下,使用 Sentinel 中間件已經能很好地應對,但 Sentinel 也并不是萬能的,需要思考其他的流控方案。

二 接口定義

為了方便,以下所有的示例代碼實現都是基于 Throttler 接口。

Throttler 接口定義了一個通用的方法用于申請單個配額。

當然你也可以定義一個 tryAcquire(String key, int permits) 簽名的方法用于一次申請多個配額,實現的思路是一樣的。

有些流控算法需要為每個 key 維護一個 Throttler 實例。

public interface Throttler {/*** 嘗試申請一個配額** @param key 申請配額的key* @return 申請成功則返回true,否則返回false*/boolean tryAcquire(String key); }

三 單機流控

1 簡單窗口

簡單窗口是我自己的命名,有些地方也叫做固定窗口,主要是為了跟后面的滑動窗口區分。

流控是為了限制指定時間間隔內能夠允許的訪問量,因此,最直觀的思路就是基于一個給定的時間窗口,維護一個計數器用于統計訪問次數,然后實現以下規則:

  • 如果訪問次數小于閾值,則代表允許訪問,訪問次數 +1。
  • 如果訪問次數超出閾值,則限制訪問,訪問次數不增。
  • 如果超過了時間窗口,計數器清零,并重置清零后的首次成功訪問時間為當前時間。這樣就確保計數器統計的是最近一個窗口的訪問量。

代碼實現 SimpleWindowThrottler

/*** 毫秒為單位的時間窗口*/ private final long windowInMs; /*** 時間窗口內最大允許的閾值*/ private final int threshold; /*** 最后一次成功請求時間*/ private long lastReqTime = System.currentTimeMillis(); /*** 計數器*/ private long counter;public boolean tryAcquire(String key) {long now = System.currentTimeMillis();// 如果當前時間已經超過了上一次訪問時間開始的時間窗口,重置計數器,以當前時間作為新窗口的起始值if (now - lastReqTime > windowInMs) { #1counter = 0;lastReqTime = now; #2}if (counter < threshold) { #3counter++; #4return true;} else {return false;} }

另外一種常見的場景是根據不同的 key 來做流控,每個 key 有單獨的時間窗口、閾值配置,因此需要為每個 key 維護一個單獨的限流器實例。

切換到多線程環境
在現實應用中,往往是多個線程來同時申請配額,為了比較簡潔地表達算法思路,示例代碼里面都沒有做并發同步控制。

以簡單窗口的實現為例,要轉換為多線程安全的流控算法,一種直接的辦法是將 tryAcquire 方法設置為 synchronized。

當然一種感覺上更高效的辦法也可以是修改讀寫變量的類型:

private volatile long lastReqTime = System.currentTimeMillis(); private LongAdder counter = new LongAdder();

不過這樣其實并不真正“安全”,設想以下的場景,兩個線程 A、線程 B 前后腳嘗試獲取配額,#1 位置的判斷條件滿足后,會同時走到 #2 位置修改 lastReqTime 值,線程 B 的賦值會覆蓋線程 A,導致時間窗口起始點向后偏移。同樣的,位置 #3 和 #4 也會構成競爭條件。當然如果對流控的精度要求不高,這種競爭也是能接受的。

臨界突變問題

簡單窗口的流控實現非常簡單,以 1 分鐘允許 100 次訪問為例,如果流量均勻保持 200 次/分鐘的訪問速率,系統的訪問量曲線大概是這樣的(按分鐘清零):

但如果流量并不均勻,假設在時間窗口開始時刻 0:00 有幾次零星的訪問,一直到 0:50 時刻,開始以 10 次/秒的速度請求,就會出現這樣的訪問量圖線:

在臨界的 20 秒內(0:50~1:10)系統承受的實際訪問量是 200 次,換句話說,最壞的情況下,在窗口臨界點附近系統會承受 2 倍的流量沖擊,這就是簡單窗口不能解決的臨界突變問題。

2 滑動窗口

如何解決簡單窗口算法的臨界突變問題?既然一個窗口統計的精度低,那么可以把整個大的時間窗口切分成更細粒度的子窗口,每個子窗口獨立統計。同時,每過一個子窗口大小的時間,就向右滑動一個子窗口。這就是滑動窗口算法的思路。

如上圖所示,將一分鐘的時間窗口切分成 6 個子窗口,每個子窗口維護一個獨立的計數器用于統計 10 秒內的訪問量,每經過 10s,時間窗口向右滑動一格。

回到簡單窗口出現臨界跳變的例子,結合上面的圖再看滑動窗口如何消除臨界突變。如果 0:50 到 1:00 時刻(對應灰色的格子)進來了 100 次請求,接下來 1:00~1:10 的 100 次請求會落到黃色的格子中,由于算法統計的是 6 個子窗口的訪問量總和,這時候總和超過設定的閾值 100,就會拒絕后面的這 100 次請求。

代碼實現(參考 Sentinel)

Sentinel 提供了一個輕量高性能的滑動窗口流控算法實現,看代碼的時候可以重點關注這幾個類:

1)功能插槽 StatisticSlot 負責記錄、統計不同緯度的 runtime 指標監控信息,例如 RT、QPS 等。

Sentinel 內部使用了 slot chain 的責任鏈設計模式,每個功能插槽 slot 有不同的功能(限流、降級、系統保護),通過 ProcessorSlotChain 串聯在一起。

參考官方 Wiki:
https://github.com/alibaba/Sentinel/wiki/Sentinel工作主流程

2)StatisticSlot 使用 StatisticNode#addPassRequest 記錄允許的請求數,包含秒和分鐘兩個維度。

3)具體記錄用到的是 Metric 接口,對應實現類 ArrayMetric,背后真正的滑動窗口數據結構是 LeapArray 。

4)LeapArray 內部維護了滑動窗口用到的關鍵屬性和結構,包括:

a)總窗口大小 intervalInMs,滑動子窗口大小 windowLengthInMs,采樣數量sampleCount:

sampleCount = intervalInMs / windowLengthInMs

當前實現默認為 2,而總窗口大小默認是 1s,也就意味著默認的滑動窗口大小是 500ms。可以通過調整采樣數量來調整統計的精度。

b)滑動窗口的數組 array,數組中每個元素以 WindowWrap 表示,其中包含:

  • windowStart:滑動窗口的開始時間。
  • windowLength:滑動窗口的長度。
  • value:滑動窗口記錄的內容,泛型表示,關鍵的一類就是 MetricBucket,里面包含了一組 LongAdder 用于記錄不同類型的數據,例如請求通過數、請求阻塞數、請求異常數等等。

記錄請求的邏輯說白了,就是根據當前時間獲取所屬的滑動窗口,然后將該窗口的統計值 +1 即可。但實際上,獲取當前所屬的時間窗口這一步隱含了不少細節,詳細的實現可以從 LeapArray#currentWindow 中找到,源碼的注釋寫得很詳細,這里就不多提了。

這里借助一張其他同學畫的圖表述以上的流程:

以上的流程基于 3.9.21 版本的源碼,早先版本的 Sentinel 內部版本實現不盡相同,使用了一個叫 SentinelRollingNumber 的數據結構,但原理是類似的。

精度問題

現在思考這么一個問題:滑動窗口算法能否精準地控制任意給定時間窗口 T 內的訪問量不大于 N?

答案是否定的,還是將 1 分鐘分成 6 個 10 秒大小的子窗口的例子,假設請求的速率現在是 20 次/秒,從 0:05 時刻開始進入,那么在 0:05~0:10 時間段內會放進 100 個請求,同時接下來的請求都會被限流,直到 1:00 時刻窗口滑動,在 1:00~1:05 時刻繼續放進 100 個請求。如果把 0:05~1:05 看作是 1 分鐘的時間窗口,那么這個窗口內實際的請求量是 200,超出了給定的閾值 100。

如果要追求更高的精度,理論上只需要把滑動窗口切分得更細。像 Sentinel 中就可以通過修改單位時間內的采樣數量 sampleCount 值來設置精度,這個值一般根據業務的需求來定,以達到在精度和內存消耗之間的平衡。

平滑度問題

使用滑動窗口算法限制流量時,我們經常會看到像下面一樣的流量曲線。

突發的大流量在窗口開始不久就直接把限流的閾值打滿,導致剩余的窗口內所有請求都無法通過。在時間窗口的單位比較大時(例如以分為單位進行流控),這種問題的影響就比較大了。在實際應用中我們要的限流效果往往不是把流量一下子掐斷,而是讓流量平滑地進入系統當中。

3 漏桶

滑動窗口無法很好地解決平滑度問題,再回過頭看我們對于平滑度的訴求,當流量超過一定范圍后,我們想要的效果不是一下子切斷流量,而是將流量控制在系統能承受的一定的速度內。假設平均訪問速率為 v, 那我們要做的流控其實是流速控制,即控制平均訪問速率 v ≤ N / T。

在網絡通信中常常用到漏桶算法來實現流量整形。漏桶算法的思路就是基于流速來做控制。想象一下上學時經常做的水池一邊抽水一邊注水的應用題,把水池換成水桶(還是底下有洞一注水就開始漏的那種),把請求看作是往桶里注水,桶底漏出的水代表離開緩沖區被服務器處理的請求,桶口溢出的水代表被丟棄的請求。在概念上類比:

  • 最大允許請求數 N:桶的大小
  • 時間窗口大小 T:一整桶水漏完的時間
  • 最大訪問速率 V:一整桶水漏完的速度,即 N/T
  • 請求被限流:桶注水的速度比漏水的速度快,最終導致桶內水溢出

假設起始時刻桶是空的,每次訪問都會往桶里注入一單位體積的水量,那么當我們以小于等于 N/T 的速度往桶里注水時,桶內的水就永遠不會溢出。反之,一旦實際注水速度超過漏水速度,桶里就會產生越來越多的積水,直到溢出為止。同時漏水的速度永遠被控制在 N/T 以內,這就實現了平滑流量的目的。

漏桶算法的訪問速率曲線如下:

附上一張網上常見的漏桶算法原題圖:

代碼實現 LeakyBucketThrottler

/*** 當前桶內剩余的水*/ private long left; /*** 上次成功注水的時間戳*/ private long lastInjectTime = System.currentTimeMillis(); /*** 桶的容量*/ private long capacity; /*** 一桶水漏完的時間*/ private long duration; /*** 桶漏水的速度,即 capacity / duration*/ private double velocity;public boolean tryAcquire(String key) {long now = System.currentTimeMillis();// 當前剩余的水 = 之前的剩余水量 - 過去這段時間內漏掉的水量// 過去這段時間內漏掉的水量 = (當前時間-上次注水時間) * 漏水速度// 如果當前時間相比上次注水時間相隔太久(一直沒有注水),桶內的剩余水量就是0(漏完了)left = Math.max(0, left - (long)((now - lastInjectTime) * velocity));// 往當前水量基礎上注一單位水,只要沒有溢出就代表可以訪問if (left + 1 <= capacity) {lastInjectTime = now;left++;return true;} else {return false;} }

漏桶的問題

漏桶的優勢在于能夠平滑流量,如果流量不是均勻的,那么漏桶算法與滑動窗口算法一樣無法做到真正的精確控制。極端情況下,漏桶在時間窗口 T 內也會放進相當于 2 倍閾值 N 的流量。

設想一下,如果訪問量相比窗口大小 N 大很多,在窗口(0~T)一開始的 0 時刻就直接涌進來,使得漏桶在時間 t( 0≈t

雖然可以通過限制桶大小的方式使得訪問量控制在 N 以內,但這樣做的副作用是流量在還未達到限制條件就被禁止。

還有一個隱含的約束是,漏桶漏水的速度最好是一個整數值(即容量 N 能夠整除時間窗口大小 T ),否則在計算剩余水量時會有些許誤差。

4 令牌桶

漏桶模型中,請求來了是往桶里注水,如果反一下,把請求放行變成從桶里抽水,對應的,把注水看作是補充系統可承受流量的話,漏桶模型就變成了令牌桶模型。

理解漏桶之后,再看令牌桶就很簡單了,抄一段令牌桶的原理:

令牌桶算法的原理是系統以恒定的速率產生令牌,然后把令牌放到令牌桶中,令牌桶有一個容量,當令牌桶滿了的時候,再向其中放令牌,那么多余的令牌會被丟棄;當想要處理一個請求的時候,需要從令牌桶中取出一個令牌,如果此時令牌桶中沒有令牌,那么則拒絕該請求。

代碼實現 TokenBucketThrottler

令牌桶與漏桶本質上是一樣的,因此漏桶的代碼稍微改下就可以變成令牌桶。

long now = System.currentTimeMillis(); left = Math.min(capacity, left + (long)((now - lastInjectTime) * velocity)); if (left - 1 > 0) {lastInjectTime = now;left--;return true; } else {return false; }

生產環境中使用令牌桶的話,可以考慮借助 Guava 中提供的 RateLimiter。它的實現是多線程安全的,調用 RateLimiter#acquire 時,如果剩余令牌不足,會阻塞線程一段時間直至有足夠的可用令牌(而不是直接拒絕,這在某些場景下很有用)。除去默認的 SmoothBursty 策略外,RateLimiter 還提供了一種叫 SmoothWarmingUp 的策略,支持設置一個熱身期,熱身期內,RateLimiter 會平滑地將放令牌的速率加大,直致最大速率。設計這個的意圖是為了滿足那種資源提供方需要熱身時間,而不是每次訪問都能提供穩定速率的服務的情況(比如帶緩存服務,需要定期刷新緩存) 。RateLimiter 有一個缺點是只支持 QPS 級別。

漏桶、令牌桶的區別

雖然兩者本質上只是反轉了一下,不過在實際使用中,適用的場景稍有差別:

1)漏桶:用于控制網絡中的速率。在該算法中,輸入速率可以變化,但輸出速率保持恒定。常常配合一個 FIFO 隊列使用。

想象一下,漏桶的破洞是固定大小的,因此漏水的速率是可以保持恒定的。

2)令牌桶:按照固定速率往桶中添加令牌,允許輸出速率根據突發大小而變化。

舉個例子,一個系統限制 60 秒內的最大訪問量是 60 次,換算速率是 1 次/秒,如果在一段時間內沒有訪問量,那么對漏桶而言此刻是空的。現在,一瞬間涌入 60 個請求,那么流量整形后,漏桶會以每秒 1 個請求的速度,花上 1 分鐘將 60 個請求漏給下游。換成令牌桶的話,則是從令牌桶中一次性取走 60 個令牌,一下子塞給下游。

5 滑動日志

一般情況下,上述的算法已經能很好地用于大部分實際應用場景了,很少有場景需要真正完全精確的控制(即任意給定時間窗口T內請求量不大于 N )。如果要精確控制的話,我們需要記錄每一次用戶請求日志,當每次流控判斷時,取出最近時間窗口內的日志數,看是否大于流控閾值。這就是滑動日志的算法思路。

設想某一個時刻 t 有一個請求,要判斷是否允許,我們要看的其實是過去 t - N 時間段內是否有大于等于 N 個請求被放行,因此只要系統維護一個隊列 q,里面記錄每一個請求的時間,理論上就可以計算出從 t - N 時刻開始的請求數。

考慮到只需關心當前時間之前最長 T 時間內的記錄,因此隊列 q 的長度可以動態變化,并且隊列中最多只記錄 N 條訪問,因此隊列長度的最大值為 N。

滑動日志與滑動窗口非常像,區別在于滑動日志的滑動是根據日志記錄的時間做動態滑動,而滑動窗口是根據子窗口的大小,以子窗口維度滑動。

偽代碼實現

算法的偽代碼表示如下:

# 初始化 counter = 0 q = []# 請求處理流程 # 1.找到隊列中第一個時間戳>=t-T的請求,即以當前時間t截止的時間窗口T內的最早請求 t = now start = findWindowStart(q, t)# 2.截斷隊列,只保留最近T時間窗口內的記錄和計數值 q = q[start, q.length - 1] counter -= start# 3.判斷是否放行,如果允許放行則將這次請求加到隊列 q 的末尾 if counter < thresholdpush(q, t)counter++# 放行 else# 限流

findWindowStart 的實現依賴于隊列 q 使用的數據結構,以簡單的數組為例,可以使用二分查找等方式。后面也會看到使用其他數據結構如何實現。

如果用數組實現,一個難點可能是如何截斷一個隊列,一種可行的思路是使用一組頭尾指針 head 和 tail 分別指向數組中最近和最早的有效記錄索引來解決, findWindowStart 的實現就變成在 tail 和 head 之間查找對應元素。

復雜度問題

雖然算法解決了精確度問題,但代價也是顯而易見的。

首先,我們要保存一個長度最大為 N 的隊列,這意味著空間復雜度達到 O(N),如果要針對不同的 key 做流控,那么空間上會占用更多。當然,可以對不活躍 key 的隊列進行復用來降低內存消耗。

其次,我們需要在隊列中確定時間窗口,即通過 findWindowStart 方法尋找不早于當前時間戳 t - N 的請求記錄。以二分查找為例,時間復雜度是 O(logN)。

四 分布式流控

現實中的應用服務往往是分布式部署的,如果共用的資源(例如數據庫)或者依賴的下游服務有流量限制,那么分布式流控就要派上用場了。

雖然可以給每臺應用服務器平均分配流控配額,把問題轉換為單機流控,但如果碰到流量不均勻、機器宕機、臨時擴縮容等場景,這種做法的效果不佳。

分布式環境下做流控的核心算法思路其實與單機流控是一致的,區別在于需要實現一種同步機制來保證全局配額。同步機制的實現可以有中心化和去中心化兩種思路:

1)中心化:配額由一個中心系統統一管控,應用進程通過向中心系統申請的方式獲取流控配額。

  • 狀態的一致性在中心系統維護,實現簡單。
  • 中心系統節點的不可用會導致流控出錯,需要有額外的保護。例如,中心化流控在中心存儲不可用時,往往會退化為單機流控。

2)去中心化:應用進程獨立保存和維護流控配額狀態,集群內周期性異步通訊以保持狀態一致。

  • 相比中心化方案,去中心化方案能夠降低中心化單點可靠性帶來的影響,但實現上比較復雜,狀態的一致性難以保證。
  • 在 CAP 中去中心化更加傾向于 A 而中心化更傾向于 C。

去中心化方案在生產環境中沒有見過,因此下文只討論中心化流控的思路。

1 接入層入口流控

應用接入的網絡架構中,在應用服務器之前往往有一層 LVS 或 Nginx 做統一入口,可以在這一層做入口的流控。本質上這就是單機流控的場景。

以 Nginx 為例,Nginx 提供了 ngx_http_limit_req_module 模塊用于流控,底層使用的是漏桶算法。

一個 Nginx 流控配置的示例如下,表示每個 IP 地址每秒只能請求 10 次 /login/ 接口。

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;server {location /login/ {limit_req zone=mylimit;proxy_pass http://my_upstream;} }

Nginx 的流控指令還支持更多配置,比如說配置 limit_req 指令時加上 burst 和 nodelay 參數來允許一定程度的突發,或者結合 geo 和 map 指令來實現黑白名單流控,具體可以參考 Nginx 官方文檔:
Rate Limiting with NGINX and NGINX Plus(https://www.nginx.com/blog/rate-limiting-nginx/)。

如果自帶的模塊不能滿足,那就上自定義的 lua 模塊吧,參考 OpenResty 提供的 Lua 限流模塊 lua-resty-limit-traffic。

2 TokenServer 流控

這里借用了 Sentinel 中的 TokenServer 叫法,Sentinel 集群流控的介紹可以參考官方文檔:Sentinel集群流控(https://github.com/alibaba/Sentinel/wiki/集群流控)。

這類流控的思路是找一個 TokenServer 來專門來管控流控配額,包括統計總調用量,判斷單個請求是否允許等等,應用服務器作為客戶端與 TokenServer 通信來獲取配額。因為流控的邏輯在 TokenServer 內部統一處理,因此單機流控中討論的算法同樣適用。

很自然地能想到,這類流控非常依賴于 TokenServer 的性能和可用性。

性能方面,單點的 TokenServer 很容易成為瓶頸,查 Sentinel 源碼,其中使用了 Netty 來做網絡通信,數據包采用自定義格式,其他性能優化能找到的不多。

可用性方面,就像 Sentinel 官方文檔中講的,若在生產環境使用 TokenServer 集群限流,必須要解決以下問題:

Token Server 自動管理、調度(分配/選舉 Token Server)

Token Server 高可用,在某個 Server 不可用時自動 failover 到其它機器

目前 Sentinel 的 TokenServer 默認并沒有實現這些能力,需要定制或增加其他系統來實現,例如,采用一種分布式一致性協議來做集群選舉,或者借助一組 monitor 來監控狀態,實現成本還是挺高的。

3 存儲式流控

存儲式流控的思想是通過一個存儲系統來保存流控的計數值等統計信息,應用從存儲中獲取統計信息,然后將最新的請求信息再寫入存儲中。存儲系統可以選擇現成的 MySQL 數據庫或者 Redis 緩存等,一般從性能出發選擇緩存的比較多。這里選擇 Tair 和 Redis 做例子。

Tair 流控

比較簡單,直接上代碼實現。

public boolean tryAcquire(String key) {// 以秒為單位構建tair的keyString wrappedKey = wrapKey(key);// 每次請求+1,初始值為0,key的有效期設置5sResult<Integer> result = tairManager.incr(NAMESPACE, wrappedKey, 1, 0, 5);return result.isSuccess() && result.getValue() <= threshold; }private String wrapKey(String key) {long sec = System.currentTimeMillis() / 1000L;return key + ":" + sec; }

是不是感覺太簡單了點?得益于 Tair 的高性能,這種方式可以很好地支撐大流量。

這種 Tair 流控的方案實際上用的簡單窗口的思路,每個 key 以每秒為一個時間窗口做 QPS 控制(QPM/QPD 原理類似)。關鍵在于用到了 Tair 的這個 API:

incr

Result incr(int namespace, Serializable key, int value, int defaultValue, int expireTime)
描述
增加計數。注意:incr 前不要 put!!
參數
namespace - 申請時分配的 namespace
key - key 列表,不超過 1k
value - 增加量
defaultValue - 第一次調用 incr 時的 key 的 count 初始值,第一次返回的值為 defaultValue + value。
expireTime - 數據過期時間,單位為秒,可設相對時間或絕對時間(Unix 時間戳)。expireTime = 0,表示數據永不過期。expireTime > 0,表示設置過期時間。若 expireTime > 當前時間的時間戳,則表示使用絕對時間,否則使用相對時間。expireTime < 0,表示不關注過期時間,若之前設過過期時間,則已之前的過期時間為準,若沒有,則作為永不過期處理,但當前 mdb 統一當做永不過期來處理。
返回值
Result 對象,返回值可為負值。當 key 不存在時,第一次返回 defaultValue+ value。后續的 incr 基于該值增加 value。

當然這種方式也有缺點:

  • 簡單窗口的臨界突變問題。
  • Tair 的可靠性問題,需要有降級方案。上面其實也說了,中心化的流控一般都需要搭配降級的單機流控。
  • 集群機器的時間同步問題。由于生成 key 會用到集群機器的本地時間,因此要求機器時間必須是一致的。

打個比方,不同機器時間稍微差個 10ms,在時間窗口的間隔點上的統計就會產生比較大的誤差,比如說在同一時刻,一臺機器時間是 0.990,一臺是 1.000,兩者調用 incr 時操作的 key 不一樣,精度自然就會受影響。

Redis 流控

Redis 支持豐富的數據結構,性能也不錯,其“單進程”模型方便同步控制,因此非常適合用來做分布式流控的存儲。

1)簡單窗口實現

使用 Redis 實現簡單窗口流控的思路跟使用 Tair 是一致的。Redis 也提供了 INCR 命令用于計數,同時 Redis 的“單進程”模型也提供了很好的并發保護。Redis 的官方文檔就寫了如何使用 INCR 來實現 Rate Limiter,我這里稍作翻譯了下:

Redis INCR key(https://redis.io/commands/incr)

以簡單窗口為例,最簡單直接的實現如下:

FUNCTION LIMIT_API_CALL(ip) ts = CURRENT_UNIX_TIME() keyname = ip+":"+ts current = GET(keyname) IF current != NULL AND current > 10 THENERROR "too many requests per second" ELSEMULTIINCR(keyname,1)EXPIRE(keyname,10)EXECPERFORM_API_CALL() END

實現上與上述的 Tair 類似,也是對每個 key 以秒為單位維護一個計數器,差別在于因為 Redis 沒有提供原子的 INCR + EXPIRE 指令,所以在 INCR 之后需要再調用一次 EXPIRE 來設置 key 的有效期。同時在外層以 MULTI 和 EXEC 包裹以保證事務性。

如果不想每次都調用 EXPIRE,可以考慮第二種方式:

FUNCTION LIMIT_API_CALL(ip): current = GET(ip) IF current != NULL AND current > 10 THENERROR "too many requests per second" ELSEvalue = INCR(ip)IF value == 1 THENEXPIRE(ip,1)ENDPERFORM_API_CALL() END

計數器的有效期在第一次 INCR 時設置為 1s,因此不需要對 key 進行額外處理。

不過需要注意的是,這種方式存在一種隱藏的競爭條件。如果客戶端在第一次調用了 INCR 后,由于應用崩潰或其他原因沒有調用 EXPIRE,計數器會一直存在。

針對方式二的這個問題,可以用 lua 腳本解決:

local current current = redis.call("incr",KEYS[1]) if tonumber(current) == 1 thenredis.call("expire",KEYS[1],1) end

第三種方式是通過 Redis 的 list 結構來實現。更復雜一些但可以記錄下每次的請求。

FUNCTION LIMIT_API_CALL(ip) current = LLEN(ip) IF current > 10 THENERROR "too many requests per second" ELSEIF EXISTS(ip) == FALSE #1MULTIRPUSH(ip,ip)EXPIRE(ip,1)EXECELSERPUSHX(ip,ip)ENDPERFORM_API_CALL() END

這里也有一個隱含的競爭條件,在執行到 EXIST 判斷這一行(#1 位置)時,兩個客戶端的 EXIST 命令可能都會返回 false,因此 MULTI/EXEC 塊里的命令會被執行兩次,不過這種情況很少出現,不太會影響計數器的準確性。

上述的幾種方式還可以進一步優化,因為 INCR 和 RPUSH 這些命令都會返回操作后的計數器值,所以可以使用 set-then-get 的方式獲取計數器值。

將簡單窗口改造成滑動窗口也是類似的思路,把單一的 key 換成一個 hash 結構,hash 里面為每個子窗口保存一個計數值,在統計時,將同個 hash 中所有子窗口的計數值相加即可。

2)令牌桶/漏桶實現

用 Redis 實現令牌桶或者漏桶也非常簡單。以令牌桶為例,在實現上,可以用兩個 key 分別存儲每個用戶的可用 token 數和上次請求時間,另一種可能更好的辦法是使用 Redis 的 hash 數據結構。

下圖的示例是一個用戶 user_1 當前在 Redis 中保存的流控配額數據:令牌桶中當前剩余 2 個 token,最近一次訪問的時間戳是 1490868000。

當收到一個新請求時,Redis 客戶端要執行的操作與我們在單機流控算法中看到的一樣。首先,從對應 hash 中獲得當前配額數據(HGETALL),根據當前時間戳、上次請求的時間戳和 token 填充速度計算要填充的 token 數;然后,判斷是否放行,更新新的時間戳和 token 數(HMSET)。

一個示例如下:

同樣的,如果要求比較高的精度,這里必須要對客戶端的操作做并發控制。

不做同步控制可能導致的問題示例:桶里只有一個 token,兩個客戶端同時請求時出現并發沖突,結果是請求都會放行。

lua 代碼示例如下:

local tokens_key = KEYS[1] local timestamp_key = KEYS[2]local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local requested = tonumber(ARGV[4])local fill_time = capacity/rate local ttl = math.floor(fill_time*2)local last_tokens = tonumber(redis.call("get", tokens_key)) if last_tokens == nil thenlast_tokens = capacity endlocal last_refreshed = tonumber(redis.call("get", timestamp_key)) if last_refreshed == nil thenlast_refreshed = 0 endlocal delta = math.max(0, now-last_refreshed) local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) local allowed = filled_tokens >= requested local new_tokens = filled_tokens if allowed thennew_tokens = filled_tokens - requested endredis.call("setex", tokens_key, ttl, new_tokens) redis.call("setex", timestamp_key, ttl, now)return { allowed, new_tokens }

3)滑動日志實現

得益于 Redis 的 Sorted Set 結構,實現滑動日志變得異常簡單。流程大致如下:

a)每個用戶有一個對應的 Sorted Set 記錄請求日志。

  • 其中每個元素的 key 和 value 可以是相同的,即請求的時間戳。
  • Sorted Set 可以根據時間窗口大小設置有效期,比如時間窗口為 1s 時設置過期時間 5s,在請求量不大時可以節省 Redis 服務器內存。

b)當收到一個新的用戶請求時,首先通過 ZREMRANGEBYSCORE 命令刪除 Sorted Set 中過期的元素,這里的過期即:

請求時間戳 t < 當前時間戳 now - 時間窗口大小 interval

c)使用 ZADD 將當前請求添加到 Set 中。

d)使用 ZCOUNT 獲取當前剩余 Set 大小,判斷是否需要流控。

long now = System.currentTimeMillis(); long maxScoreMs = now - windowInSecond * 1000;Transaction redis = jedisPool.getResource().multi(); redis.zremrangeByScore(key, 0, maxScoreMs); redis.zadd(key, now, now + "-" + Math.random()); // 加入一個隨機值使得member不重復 redis.expire(key, windowInSecond); redis.exec();

另一個 JS 實現的代碼示例:
https://github.com/peterkhayes/rolling-rate-limiter/blob/master/index.js

由于滑動日志算法的空間復雜度較其他算法高,使用滑動日志算法時,需要注意監控 Redis 內存的使用量。

4)并發控制

上面的幾種算法都提到了不做并發控制可能帶來的競態條件,但額外的并發控制必然會帶來性能下降,通常需要在精度和性能之間做取舍。Redis 流控的并發控制常見的有幾類:

  • 使用 Redis 事務 MULTI/EXEC。
  • 使用 RedLock(https://redis.io/topics/distlock) 等分布式鎖,要求每個客戶端操作前先獲取對應 key 的分布式鎖。
  • Lua 腳本。

最好通過性能測試來決定使用哪一種方式。

4 擴展的一些思考

分布式流控帶來了網絡通信、加鎖同步等開銷,會對性能帶來一定影響。同時分布式環境的可靠性也會帶來更多挑戰。如何設計一個高性能、高可靠性的分布式流控系統?這可能是個涉及到整個系統方方面面的大話題。

分享一下個人的一些思考,歡迎討論:

1)根據實際訴求,合理搭配不同層的多級流控是個不錯的方式,盡量把流量攔在外層。例如常見的接口層 Nginx 流控 + 應用層流控。

2)選擇一個合適的緩存系統保存流控的動態數據,這個一般跟著公司的統一技術架構走。

3)將流控的靜態配置放到配置中心(例如 Diamond)。

4)設計時要考慮分布式流控不可用的情況(例如緩存掛掉),必要時切到單機流控,使用 Sentinel 成熟可靠。

5)很多時候對精度的要求沒那么高,因為一般都會允許一定的突發量。這時候可以做一些性能的優化。性能的最大瓶頸在于每次請求都會訪問一次緩存,我之前在設計時就采用了一種折中的辦法:

  • 將可用配額的一部分,按一定比例(例如 50%),先預分配給集群內的機器。一般是平均分配,如果預先就已經知道每臺機器的流量權重,可以加權分配。每臺機器消耗配額的速率不同,中間也可能有機器宕機,可能有擴縮容,因此預分配的比例不宜太大,當然也不宜太小。
  • 每臺機器在配額耗盡時,向中心系統請求配額,這里的一個優化點是每臺機器會記錄自身配額消耗的速率(等同于承受的流量速率),按照速率大小申請不同大小的配額,消耗速率大則一次性申請更多。
  • 在整體可用配額不足一定比例時(例如 10%),限制每臺機器一次可申請的配額數,按剩余窗口大小計算發放配額的大小,并且每次發放量不超過剩余配額的一定比例(例如 50%),使得剩余的流量能夠平滑地過渡。

五 總結

分布式流控的算法其實是單機流控的延伸,算法本質是一樣的。這里按我的個人理解總結了上述幾種流控算法的復雜度和適用場景。

原文鏈接:https://developer.aliyun.com/article/765912?

版權聲明:本文中所有內容均屬于阿里云開發者社區所有,任何媒體、網站或個人未經阿里云開發者社區協議授權不得轉載、鏈接、轉貼或以其他方式復制發布/發表。申請授權請郵件developerteam@list.alibaba-inc.com,已獲得阿里云開發者社區協議授權的媒體、網站,在轉載使用時必須注明"稿件來源:阿里云開發者社區,原文作者姓名",違者本社區將依法追究責任。 如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至:developer2020@service.aliyun.com 進行舉報,并提供相關證據,一經查實,本社區將立刻刪除涉嫌侵權內容。

總結

以上是生活随笔為你收集整理的单机和分布式场景下,有哪些流控方案?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

日韩中文字幕免费在线播放 | 亚洲最新毛片 | 国产欧美日韩一区 | 国产a高清 | 久久久久久久久久久国产精品 | 91在线亚洲 | 成人av一区二区在线观看 | 在线观看免费色 | 91精品在线麻豆 | 亚洲精品乱码久久久久久蜜桃欧美 | 波多野结衣一区二区三区中文字幕 | 久久综合九九 | 午夜视频99 | 天天操狠狠操网站 | 国产麻豆视频在线观看 | 亚洲成人黄色在线 | 日韩黄色大片在线观看 | 久久99国产精品自在自在app | 午夜婷婷在线播放 | 91传媒91久久久 | 久久久毛片 | 丁香免费视频 | 久久久久久久久黄色 | 女人高潮特级毛片 | 久久久久国产免费免费 | 国产一级特黄电影 | 在线va视频 | 久久久人 | 免费观看不卡av | 免费99视频 | 国产成人一区二区三区在线观看 | 久久久久激情电影 | 激情久久综合 | 国产精品s色 | av在线亚洲天堂 | 国产91在线播放 | 国产在线一区二区 | 日韩精品一区二区三区丰满 | 手机成人在线 | 欧美激情精品一区 | www视频在线播放 | 99久久精品国产观看 | 免费看片在线观看 | 永久免费毛片在线观看 | 国产精品99久久久久久久久久久久 | av免费试看| 国产美女视频一区 | 欧美国产日韩一区二区 | 欧美视频99 | 亚洲精品456在线播放第一页 | 国产a级片免费观看 | 国产小视频免费在线网址 | 日韩一二区在线观看 | 色综合亚洲精品激情狠狠 | 国产无遮挡又黄又爽在线观看 | 久久午夜国产精品 | 亚洲美女免费精品视频在线观看 | 黄网站大全 | 国产精品第一页在线观看 | 中文字幕免费不卡视频 | 婷婷中文字幕 | 日韩系列 | 麻豆91在线看 | 成人av免费在线看 | 中文字幕av免费 | 欧美作爱视频 | 国产精品国产三级国产aⅴ9色 | 久久精品艹 | 五月天久久婷 | 成人v| 久久精品国亚洲 | 在线精品一区二区 | 欧美激情视频三区 | 久久尤物电影视频在线观看 | 亚洲日本va午夜在线影院 | 久久精品视频网址 | 9999精品免费视频 | 国产精品久久久久久久av电影 | 国产在线精品一区 | 亚洲国产婷婷 | 成年人在线观看 | 国产精品久久久久久久久久了 | 久久精品爱爱视频 | 久久99影院 | 99中文字幕视频 | 日韩精品在线免费播放 | 毛片美女网站 | 性色av一区二区三区在线观看 | 国产视频第二页 | 久久久久国产成人精品亚洲午夜 | jizzjizzjizz亚洲| 免费a级大片 | 日韩毛片在线播放 | 蜜桃视频在线观看一区 | 99中文字幕视频 | 久99久中文字幕在线 | 国产成人在线精品 | 美女久久| 精品国产欧美一区二区 | 97福利在线 | 黄色电影网站在线观看 | 在线观看免费av片 | 精品久久一区 | 亚洲撸撸 | 五月婷婷亚洲 | 国产色综合 | 亚洲春色成人 | 97视频人人免费看 | 亚洲 精品在线视频 | 成 人 黄 色 免费播放 | 久久呀 | 亚洲午夜精品一区 | 人人插人人舔 | 天天操天天操天天操天天操天天操 | 精品视频在线看 | 亚洲五月激情 | 五月天色婷婷丁香 | 久久精品99国产精品酒店日本 | 久草视频资源 | 国产又粗又猛又爽 | www国产一区 | 久久久精品成人 | 欧美最猛性xxx | 国产99久久久精品视频 | 911在线| 91女神的呻吟细腰翘臀美女 | 国产尤物一区二区三区 | 爱色av.com | 亚洲欧洲日韩在线观看 | 久久精品a | 午夜久久久久久久久久影院 | 99免费在线视频观看 | 久久国产精品一区二区三区四区 | 色噜噜日韩精品一区二区三区视频 | 中文字幕视频一区二区 | 久久精品久久久久 | 久久男人视频 | 久久免费精彩视频 | 日韩免费不卡av | 黄色成人av网址 | 最新av观看 | 国产老熟 | 天天插夜夜操 | a级国产乱理伦片在线播放 久久久久国产精品一区 | 婷婷综合五月天 | 国产亚洲小视频 | 日本中文字幕在线看 | 色综合久久88色综合天天免费 | 日韩a欧美| 69亚洲乱 | 国产精品一区二区三区99 | 色婷婷99| 97成人精品 | a黄色影院 | 国产做爰视频 | 亚洲精品99久久久久中文字幕 | av在线色 | 国产精品久久久久久久久久久久午 | 欧美精品一区二区在线观看 | 天天操天天干天天玩 | 中文字幕免费在线看 | 国产精品欧美激情在线观看 | 国产色综合天天综合网 | 欧美久久久久久久 | 亚洲精品乱码久久久久久高潮 | 天天色天天综合 | 视频在线观看入口黄最新永久免费国产 | 久操免费视频 | 高清不卡一区二区在线 | 亚洲欧美日韩在线一区二区 | 成片视频在线观看 | 一级片视频免费观看 | 超碰97人人干 | 国产女做a爱免费视频 | 免费福利在线视频 | 精品九九九 | a级国产乱理伦片在线观看 亚洲3级 | www.av小说 | 最新一区二区三区 | 超碰在线1 | 天天操天天操天天操天天操天天操天天操 | 免费视频a| 日本精品久久久久中文字幕5 | 国产免费一区二区三区网站免费 | 涩涩网站在线播放 | 性日韩欧美在线视频 | 一区二区三区免费看 | 久久一区二区三区超碰国产精品 | 五月婷婷久草 | 日韩欧美一区二区三区视频 | 国色天香av | 欧美a级免费视频 | 黄色中文字幕在线 | 国产成人精品一二三区 | 中文字幕在线观看完整版电影 | 天天伊人网 | 成人av免费 | 天堂中文在线播放 | 日韩免费av在线 | 五月婷婷天堂 | 国内丰满少妇猛烈精品播 | 免费亚洲黄色 | 色网站免费在线看 | 亚洲欧美视频在线播放 | 在线免费观看一区二区三区 | 青青草国产在线 | 免费日韩高清 | 永久免费精品视频网站 | 在线免费观看麻豆视频 | 波多野结依在线观看 | 91麻豆精品国产自产在线游戏 | 一区二区三区手机在线观看 | 国产精品久久久久久久久久东京 | 香蕉91视频 | 日韩激情影院 | 久久精品999| 欧美日韩中文在线视频 | 成人h在线观看 | 久久专区 | 999日韩| 天天曰视频| 中文字幕在线看视频 | 国产精品永久免费 | 婷香五月 | 99视频导航 | 91热精品| 三级黄色三级 | 人人插人人玩 | 日韩理论电影在线 | 色永久免费视频 | 日韩性久久 | 国产免费一区二区三区网站免费 | 五月婷婷丁香在线观看 | 中文字幕一区二区三区在线观看 | 久久久福利影院 | 国产精品片 | 久草在线网址 | 97电院网手机版 | 五月婷婷一区 | www.亚洲视频 | 精品美女久久久久 | 一级一片免费视频 | 91成人精品一区在线播放69 | 韩国av一区二区三区在线观看 | 午夜少妇 | 国产黄色视 | 中文字幕第一 | 深爱激情综合 | 国产一及片 | 日韩国产精品一区 | 国产麻豆果冻传媒在线观看 | 麻豆精品在线 | 国产精品成人av在线 | 免费看一级黄色 | 天天操天天操一操 | 中国一级片在线 | 天天天射 | 日韩一区正在播放 | 国产一区二区网址 | 国产亚洲午夜高清国产拍精品 | 欧洲精品在线视频 | 一级黄色av | 韩日av一区二区 | 亚洲国内精品视频 | 日韩激情久久 | 91探花视频 | 国产又粗又猛又爽 | 九九视频免费观看视频精品 | 狠狠的干狠狠的操 | 99久久久久久久久久 | 天天做日日爱夜夜爽 | 综合色中文 | 国产美腿白丝袜足在线av | 日韩一区二区三区在线观看 | 亚洲婷婷网 | 免费在线91 | 深爱激情站 | 国产精品一区二区三区观看 | 91精品国自产在线观看 | 欧美日韩国产欧美 | 91精品国产综合久久福利 | 一区二区精品国产 | 在线观看91久久久久久 | 女人高潮一级片 | 欧美a影视 | 日韩在线观看视频网站 | 色爱成人网| www久久99| 99国产在线视频 | 999成人| 在线看黄色av | av丝袜制服 | 中文字幕一区二区三区四区久久 | 婷婷激情五月综合 | 国产a国产a国产a | 久久一线| 亚洲综合一区二区精品导航 | 国产精品视频内 | 在线成人观看 | 天堂在线视频中文网 | 日本中文字幕电影在线免费观看 | 日本久久免费电影 | 国产高h视频 | 国产视频欧美视频 | 成人a免费 | 色婷婷啪啪免费在线电影观看 | 天天操夜夜干 | 久久久久久久久久久网站 | 国产又粗又猛又爽又黄的视频先 | 97超碰免费在线观看 | 欧美日韩在线精品 | 中文字幕色在线视频 | 日韩欧美一区二区在线播放 | 99在线观看免费视频精品观看 | 国产精品美女久久 | 成人毛片一区二区三区 | 在线视频一区二区 | 亚洲精品在线一区二区三区 | 久草久草在线观看 | av中文字幕日韩 | 免费观看日韩av | 久久综合毛片 | 91精品国产综合久久久久久久 | 国产免费视频一区二区裸体 | 日韩网站免费观看 | 中文字幕乱视频 | 麻豆视频免费版 | 91在线精品视频 | 精品国产一区二区三区久久 | 免费欧美精品 | 日韩在线观看你懂的 | 极品嫩模被强到高潮呻吟91 | 国产精品一区免费在线观看 | 欧美久久久一区二区三区 | 国产精品观看在线亚洲人成网 | 精品日韩在线一区 | 国产不卡av在线播放 | 久久久精品在线观看 | 手机在线看a | 中文字幕在线观看完整 | 狠狠的日 | 天天操综合网站 | 狠狠艹夜夜干 | 国产区免费在线 | 久久久久国产精品厨房 | 在线观看亚洲视频 | 久草在线精品观看 | 国产一级电影网 | 成年人黄色免费看 | 成人亚洲欧美 | 国产黄a三级三级三级三级三级 | 中文字幕在线免费播放 | 日韩一级精品 | 91香蕉国产在线观看软件 | 在线视频免费观看 | 久久精品国产精品亚洲精品 | 欧美一级日韩三级 | av在线免费不卡 | 中文字幕成人在线观看 | 麻豆视频免费播放 | 成人一级片在线观看 | aaa黄色毛片| 婷色| 国产精品自在欧美一区 | 久草视频99 | 91九色视频导航 | 黄色av成人在线观看 | 日本 在线 视频 中文 有码 | 成人四虎 | 91大片成人网 | 8090yy亚洲精品久久 | 日韩城人在线 | 日韩三级视频在线观看 | 国产精品久久久久久爽爽爽 | 99久久精品无免国产免费 | 久久国产成人午夜av影院宅 | av在线电影播放 | 久久一区精品 | 天天色综合三 | 免费看黄色毛片 | 日韩国产精品久久久久久亚洲 | 91视频在线免费下载 | 超碰免费97 | 亚洲爱视频 | 99九九99九九九视频精品 | 99热超碰 | 亚洲作爱| 久久免费黄色网址 | 国产福利免费看 | 日韩激情免费视频 | 久久av一区二区三区亚洲 | 国产亚洲视频中文字幕视频 | 在线观看播放av | 久久久久久久久久久久99 | 98福利在线 | 免费av电影网站 | 国产精品高清免费在线观看 | 韩国在线一区二区 | www黄色com| 99精品在线看 | 欧美精彩视频 | 欧美午夜性 | 亚洲欧美色婷婷 | 一区二区三区手机在线观看 | 91精品秘密在线观看 | 麻豆视屏 | 国产系列在线观看 | av免费在线网站 | 国产一级特黄毛片在线毛片 | 麻豆视频免费入口 | 日本一区二区不卡高清 | 欧美在线不卡一区 | 人人干,人人爽 | 中文十次啦 | 久久精品96 | 国产一级一片免费播放放 | 97超碰中文 | 中文字幕欧美日韩va免费视频 | 一区二区视频电影在线观看 | 久久精品国产亚洲aⅴ | 91九色视频在线播放 | 精品国产一区二区三区久久久久久 | 国产精品久久久久av免费 | 99免费在线观看视频 | 国产亚洲情侣一区二区无 | 欧美一级xxxx | 美女久久精品 | 欧美日韩午夜爽爽 | 激情综合五月天 | 日韩欧美高清 | 黄色网在线播放 | 亚洲专区免费观看 | 一本色道久久综合亚洲二区三区 | 国产精品99久久久久的智能播放 | 久久久久亚洲国产精品 | 激情一区二区三区欧美 | 婷婷九九 | 国产亚洲精品美女 | 国产福利一区二区三区视频 | 国产精品扒开做爽爽的视频 | 中文字幕刺激在线 | 国产精品久久在线观看 | 精品久久久久一区二区国产 | 天天激情 | 鲁一鲁影院 | 岛国一区在线 | 久久久久久看片 | 93久久精品日日躁夜夜躁欧美 | 国产一区在线视频 | 欧美极品少妇xbxb性爽爽视频 | 国产精品完整版 | 久草视频免费观 | 六月激情久久 | 91看片淫黄大片一级在线观看 | 天堂网一区二区三区 | 国产精品乱码久久 | 国产精品免费高清 | 免费在线观看亚洲视频 | 深爱婷婷 | 丁香花中文字幕 | 久久国产精品久久国产精品 | 国产成人在线精品 | 国产一级二级av | 国产精品久久久久一区 | 欧亚日韩精品一区二区在线 | 在线观看av国产 | 91插插插免费视频 | av网站免费看 | 在线观看成人小视频 | 久久精品播放 | 一级性视频 | a天堂最新版中文在线地址 久久99久久精品国产 | 久久久网| 久久久久 免费视频 | 国产一区二区免费看 | 日韩欧美视频在线免费观看 | 国产精品自产拍在线观看桃花 | 911国产在线观看 | 夜夜夜精品| 91九色综合 | 免费看的毛片 | 日韩精品在线免费观看 | 日韩精品第1页 | 99精品在线视频观看 | 天天射综合网视频 | 成年人在线免费看 | 日韩欧美精品在线视频 | 国内精品中文字幕 | 激情综合站 | 国产精品日韩久久久久 | 精品伦理一区二区三区 | 国产精品日韩欧美一区二区 | 国产区精品在线 | 国产中文字幕网 | 国产精品久久久久永久免费 | 狠狠亚洲| 国产精品久久久久久久久久不蜜月 | 欧美一二三区播放 | 亚洲精品国产精品国自 | 久久精品国产美女 | 久久福利小视频 | 欧美精品亚洲二区 | 国产99久久久精品 | 成 人 黄 色 免费播放 | 国产精品一区二区三区观看 | 91精选| 国产免费专区 | 日韩免费在线观看视频 | 精品国产一区二区三区久久影院 | 99热在线观看 | 伊人久久精品久久亚洲一区 | 中文字幕一区二区三区在线播放 | 天天色官网 | 国产成人99av超碰超爽 | 国内精品久久久久久久影视麻豆 | 久久久国产精品免费 | 久久精品国产免费观看 | 日日夜夜操av | 日韩在线观看网址 | 91手机视频在线 | 国产精品原创av片国产免费 | 久草线 | 中文在线a在线 | 久久天堂网站 | 久久久久欧美精品 | 在线看毛片网站 | 欧美aa一级片 | 欧美日韩一区二区三区视频 | 黄色在线观看免费网站 | 在线成人小视频 | 成人免费视频在线观看 | 黄色毛片网站在线观看 | 欧美激情综合色综合啪啪五月 | 久久久久久久久久久久久久av | 久草国产在线观看 | 精品福利在线 | 亚洲精品福利在线观看 | 99热手机在线观看 | 黄色www免费| 免费在线观看av不卡 | av解说在线观看 | 国产美女在线免费观看 | 99 色| 久久久久五月 | 在线视频日韩精品 | 亚洲成年人在线播放 | 99久久精品国产欧美主题曲 | 日韩av成人 | 国产午夜精品一区二区三区四区 | 在线免费观看黄色 | a视频在线看 | 丁香激情视频 | 亚洲欧洲美洲av | 中文字幕亚洲在线观看 | 天天操人 | 国产福利一区二区三区在线观看 | 国产精品成人aaaaa网站 | 狠狠干网 | 天天插视频 | 蜜臀av性久久久久蜜臀aⅴ涩爱 | 国产精品一区专区欧美日韩 | 日韩在线三区 | 激情综合亚洲精品 | a级免费观看 | 国产色视频一区二区三区qq号 | 国产精品入口传媒 | 精品国产乱码一区二 | 亚洲日本韩国一区二区 | 99国产精品一区二区 | 久久综合久久久 | 91在线视频导航 | 日韩免费一级a毛片在线播放一级 | 久久久久久久久久免费视频 | 国产精品久久一区二区无卡 | 欧美a在线看 | 久久久.com| 久久久91精品国产 | 91在线中文 | 中文字幕在线人 | 91网页版在线观看 | 黄色av一区二区三区 | 成人黄色免费在线观看 | 亚洲精品电影在线 | 久久av免费 | 四虎精品成人免费网站 | 99这里只有久久精品视频 | 久久久久久蜜桃一区二区 | 婷婷六月天丁香 | 97视频在线观看免费 | 久久久国产毛片 | 久久香蕉一区 | 国产女人40精品一区毛片视频 | 欧美三级高清 | 成人午夜毛片 | 激情五月网站 | 亚洲国产中文字幕 | 亚洲精品乱码久久久久久 | 久久夜色精品亚洲噜噜国4 午夜视频在线观看欧美 | 精品女同一区二区三区在线观看 | 丁香花在线观看视频在线 | 色综合中文综合网 | 亚洲激情影院 | 国产无遮挡又黄又爽在线观看 | 中文字幕日韩伦理 | 在线影院av | 亚洲高清资源 | 在线最新av | 日韩天天操 | 久久精品96| 精精国产xxxx视频在线播放 | 中文字幕在线免费看 | 久一网站| 亚洲精品在线观看网站 | 国产免费中文字幕 | av千婊在线免费观看 | 久久久精品国产一区二区 | 国产精品久久久一区二区 | 狠狠躁日日躁狂躁夜夜躁av | 国产精品自产拍在线观看网站 | 极品嫩模被强到高潮呻吟91 | 亚洲va在线va天堂 | 国产一级一级国产 | 日韩欧美在线一区 | 日韩精品视频在线免费观看 | 500部大龄熟乱视频 欧美日本三级 | 中文字幕亚洲高清 | 久久国产精品免费观看 | 欧美亚洲一区二区在线 | 91精品国产麻豆国产自产影视 | 国产精品成久久久久 | 久久久久国产成人免费精品免费 | 欧美男同视频网站 | 免费观看91视频大全 | 99精品在线视频播放 | 麻豆成人小视频 | 97超碰总站 | 国产高清福利在线 | 日本资源中文字幕在线 | 超碰在线人 | 91激情在线视频 | 91精品啪啪 | 国产精品麻豆三级一区视频 | 成人免费观看电影 | 国产精品久久久久久久久久不蜜月 | 在线亚洲欧美日韩 | 久久久这里有精品 | 国产亚洲视频在线观看 | 亚洲少妇xxxx | 天天夜夜亚洲 | 女人18毛片a级毛片一区二区 | 国产精品av免费观看 | 二区三区精品 | 看片网站黄色 | 国内精品久久久久久中文字幕 | 美女免费视频黄 | 中文 一区二区 | 五月天亚洲激情 | 九九有精品 | 成人国产精品一区二区 | 9在线观看免费高清完整版 玖玖爱免费视频 | 亚洲一区精品人人爽人人躁 | 精品国产不卡 | 国产精品久久伊人 | 亚洲精品乱码久久久久v最新版 | 伊人影院得得 | 国产亚洲在线 | 亚洲综合小说电影qvod | 欧美激情视频免费看 | 久久精品观看 | 人人玩人人添人人澡97 | 在线国产中文 | 免费在线观看视频一区 | 日韩av手机在线观看 | 日韩av视屏 | 国产二区av | 久草久草在线 | 在线欧美a| 亚洲精品在线一区二区 | 丝袜+亚洲+另类+欧美+变态 | 中文字幕日韩国产 | 中文av日韩 | 亚洲精品视频在线看 | 国产1区2区3区精品美女 | 国产字幕在线看 | 欧美大片在线观看一区 | 男女啪啪免费网站 | 最近字幕在线观看第一季 | 激情五月婷婷综合 | 成 人 黄 色 视频 免费观看 | av在线播放亚洲 | 国模视频一区二区三区 | 欧美a级在线播放 | 在线观看日韩av | 三级a视频 | 欧美一区免费在线观看 | 国产精品久久一区二区三区不卡 | 亚洲综合最新在线 | 免费福利视频网 | 亚洲精欧美一区二区精品 | 91一区二区三区在线观看 | 超碰999 | 欧美激情视频免费看 | 国产黄大片在线观看 | 三级黄色在线 | 国产成人久久精品77777 | 久久99久久99精品免观看粉嫩 | 97超碰国产在线 | 精品无人国产偷自产在线 | 在线观看黄网站 | 一级片在线| 国产美女永久免费 | 美女福利视频在线 | 亚洲精品色 | 国产 日韩 中文字幕 | 中文字幕在线观看网址 | 97在线视频免费播放 | 狠狠狠色狠狠色综合 | 欧美视频日韩 | 国产三级香港三韩国三级 | 五月婷婷激情综合 | 国产小视频国产精品 | 亚洲精品国精品久久99热一 | 人人超碰人人 | 黄色精品久久久 | 17婷婷久久www | 99视频导航 | 日韩艹 | 91大神精品视频在线观看 | 国产精品久久久久久久免费观看 | 少妇bbbb| 国产成人av在线 | 精品999久久久 | 成人小视频在线免费观看 | 91av视频在线免费观看 | 99热在线国产 | 精品国产伦一区二区三区免费 | 最近久乱中文字幕 | 亚洲视频观看 | 久久久亚洲麻豆日韩精品一区三区 | av线上看| 91麻豆看国产在线紧急地址 | 日韩av免费观看网站 | 涩涩资源网 | 中文字幕在线观看网站 | 久草视频在线资源站 | 最新日韩视频在线观看 | 91毛片在线 | 欧美色图亚洲图片 | 1024手机基地在线观看 | 久久视频免费看 | 国产录像在线观看 | 国产精品一区二区久久久 | 精品一区电影国产 | 久久伦理| 在线电影av| 国产精品日韩久久久久 | 免费看国产视频 | 亚洲精品国偷拍自产在线观看蜜桃 | 国产精品成人自产拍在线观看 | 国产又粗又猛又黄 | 99色视频在线 | 久青草国产在线 | 免费在线观看av的网站 | 美女久久久久久久 | 久久久免费精品视频 | 黄网站app在线观看免费视频 | 午夜黄网 | 婷婷综合电影 | 久久高清精品 | av大全在线看 | 奇米网8888| 成人性生交大片免费看中文网站 | 国产原创在线 | 亚洲综合激情五月 | 国产毛片在线 | 欧美久久久久久久久久久久 | 精品国产久 | www色,com| 久久久久观看 | a天堂中文在线 | www.亚洲精品 | 久久在线一区 | 在线看国产视频 | www免费看| 国产视频精品免费 | 在线免费观看黄色av | 91在线视频播放 | 亚洲精品美女久久17c | 免费人成网 | 国产精品大尺度 | 久草免费福利在线观看 | 国产明星视频三级a三级点| 免费观看国产视频 | 五月婷婷在线视频 | 激情网站网址 | 草樱av | 97碰碰精品嫩模在线播放 | 欧美性一级观看 | 久久激情五月丁香伊人 | 国产精品免费一区二区三区 | 精品国产电影一区二区 | 午夜狠狠操 | 日日干美女 | 色综合天天天天做夜夜夜夜做 | 欧美亚洲国产精品久久高清浪潮 | 一级黄色大片 | 久日精品 | 国内精品久久久精品电影院 | 日日操网 | 欧美激情综合色综合啪啪五月 | 夜夜夜草 | 精品欧美一区二区精品久久 | 黄网站app在线观看免费视频 | 亚洲va在线va天堂 | 日韩欧美精品一区 | 五月综合激情婷婷 | 91成人天堂久久成人 | 亚洲精品一区二区精华 | 综合色婷婷 | 日韩免费av在线 | 三级黄色网址 | 免费观看性生交大片3 | 97在线观视频免费观看 | 亚洲少妇自拍 | 麻豆激情电影 | 日韩一区二区三区不卡 | 成人午夜毛片 | 一区二区三区免费在线观看视频 | 色综合天天色综合 | 日韩成人黄色 | 日韩国产精品一区 | 黄色一级片视频 | 国产精品av一区二区 | 天天超碰 | 国产精品毛片一区视频播不卡 | 97视频在线观看播放 | 99 精品 在线 | 日本中文字幕在线免费观看 | 中文av在线免费观看 | 久久99欧美| 国产不卡一二三区 | 国产精品一区二区久久 | 草久久影院 | 永久黄网站色视频免费观看w | 亚洲涩涩网站 | 麻豆观看| 丁香视频免费观看 | 特级西西人体444是什么意思 | 久久视频免费在线 | 欧美 日韩 国产 成人 在线 | 99精品视频中文字幕 | 久久精品电影网 | 国产精品门事件 | 日韩在线免费 | 天天操一操| 久久久高清免费视频 | 九九色在线观看 | 免费观看十分钟 | 亚洲爱爱视频 | 天堂久久电影网 | 久久理论片| 免费看污污视频的网站 | 亚洲精品国偷拍自产在线观看蜜桃 | 亚洲精品国产精品国自产 | 亚洲国产日韩欧美 | 国产第一二区 | 狠狠躁夜夜躁人人爽视频 | 国产精品九九热 | 国产精品久久久久久久午夜 | 日韩久久久久久久 | 成人小视频在线免费观看 | 天天干天天干天天干天天干天天干天天干 | 五月色丁香 | www.日本色 | a黄在线观看 | 亚洲午夜久久久久久久久久久 | 麻豆国产精品永久免费视频 | 又长又大又黑又粗欧美 | 国产精品久久久久久久久免费 | 四虎国产精品免费 | 婷婷视频| 四虎影视久久久 | 九九九九九九精品任你躁 | 国产精品一区二区三区免费视频 | 全黄网站| 日韩精品一区二区三区不卡 | 丰满少妇高潮在线观看 | av黄色大片 | 久久综合99| 久久午夜鲁丝片 | 免费国产ww | www.在线观看av | 中文字幕亚洲国产 | 日韩av高潮 | 一区二区亚洲精品 | 国产免费人成xvideos视频 | 日日夜夜天天久久 | 91亚洲影院 | 国产小视频免费观看 | 日韩成人中文字幕 | 992tv在线成人免费观看 | 91九色最新地址 | 国产很黄很色的视频 | 美女网站视频免费都是黄 | 99精品在线免费视频 | 亚洲精品午夜久久久久久久久久久 | 日日日日日 | 国产精品视频免费在线观看 | 天天色天天上天天操 | 成年人免费电影 | 国产精品露脸在线 | ,午夜性刺激免费看视频 | 黄色国产在线 | 天天色综合三 | 蜜臀av性久久久久av蜜臀妖精 | 国产男女无遮挡猛进猛出在线观看 | 日韩高清一二区 | 精品一二三四五区 | 超碰在线日韩 | 欧美日韩国产一区 | 91在线一区二区 | 国产精品一区二区免费在线观看 | 91豆花在线观看 | 毛片区| japanesexxx乱女另类 | 在线观看视频免费大全 | 日韩一区在线免费观看 | 天天操天天谢 | 日韩在线电影观看 | 欧美色图另类 | 激情开心网站 | 国产一区福利在线 | 毛片一区二区 | 欧美精品久久久久久久久久 | 999亚洲国产996395| 成人欧美日韩国产 | 国产视频在 | 91精品视频在线看 | 黄色大全在线观看 | 亚洲成av人影院 | 深夜免费小视频 | 国产视频丨精品|在线观看 国产精品久久久久久久久久久久午夜 | 在线之家免费在线观看电影 | av福利第一导航 | 久操视频在线观看 | 亚洲天天综合网 | 不卡的av| 日韩精品一区二区三区丰满 | 婷婷综合在线 | 国产精品久久久久一区 | 91麻豆精品国产91久久久使用方法 | 午夜电影中文字幕 | 久久夜av| 久久久亚洲国产精品麻豆综合天堂 | 天天操天天操天天操天天操天天操天天操 | 久草在线看片 | 国产亚洲精品成人av久久ww | 精品一区二区三区电影 | 91在线一区二区 | 精品国产乱码久久久久久1区二区 | 国产精品久久久久影视 | 五月激情丁香图片 | 精品久久一 | 69夜色精品国产69乱 | 免费在线观看一区二区三区 | 色婷婷丁香 | 亚洲国产片 | 国产精品毛片一区视频 | 久久久久久久免费观看 | 精品96久久久久久中文字幕无 | 91亚洲影院 | 国产一区欧美二区 | 视频在线91 | 久久五月婷婷丁香 | 欧美极度另类 | 在线观看视频免费播放 | 午夜视频在线网站 | 狠狠的干 | 99久久婷婷国产一区二区三区 | 日韩免费播放 | 91精品国产乱码久久桃 | 婷婷av网 | 欧美天天干 | 国产成人在线免费观看 | 亚洲色五月 | 国产高清不卡 | av在线电影网站 | 91在线中字 | av在线等| 69xx视频 | 亚洲精品高清在线 | 亚洲免费永久精品国产 | 免费观看第二部31集 | 月丁香婷婷| 天天射狠狠干 | 亚洲国产精品电影在线观看 | 日韩一级成人av | 亚洲成a人片在线观看网站口工 |