溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何設(shè)計并實現(xiàn)存儲QoS

發(fā)布時間:2021-11-23 21:42:29 來源:億速云 閱讀:210 作者:柒染 欄目:云計算

如何設(shè)計并實現(xiàn)存儲QoS,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

1. 資源搶占問題

隨著存儲架構(gòu)的調(diào)整,眾多應(yīng)用服務(wù)會運(yùn)行在同一資源池中,對外提供統(tǒng)一的存儲能力。資源池內(nèi)部可能存在多種流量類型,如上層業(yè)務(wù)的IO流量、存儲內(nèi)部的數(shù)據(jù)遷移、修復(fù)、壓縮等,不同的流量通過競爭的方式確定下發(fā)到硬件的IO順序,因此無法確保某種流量IO服務(wù)質(zhì)量,比如內(nèi)部數(shù)據(jù)遷移流量可能占用過多的帶寬影響業(yè)務(wù)流量讀寫,導(dǎo)致存儲對外提供的服務(wù)質(zhì)量下降,由于資源競爭結(jié)果的不確定性無法保障存儲對外能提供穩(wěn)定的集群環(huán)境。

如下面交通圖所示,車輛逆行、加塞隨心隨遇,行人橫穿、閑聊肆無忌憚,最終出現(xiàn)交通擁堵甚至安全事故。

如何設(shè)計并實現(xiàn)存儲QoS

2. 如何解決資源搶占

類比上一幅交通圖,如何規(guī)避這樣的現(xiàn)象大家可能都有自己的一些看法,這里先引入兩個名詞

  • QoS,即服務(wù)質(zhì)量,根據(jù)不同服務(wù)類型的不同需求提供端到端的服務(wù)質(zhì)量。

  • 存儲QoS,在保障服務(wù)帶寬與IOPS的情況下,合理分配存儲資源,有效緩解或控制應(yīng)用服務(wù)對資源的搶占,實現(xiàn)流量監(jiān)控、資源合理分配、重要服務(wù)質(zhì)量保證以及內(nèi)部流量規(guī)避等效果,是存儲領(lǐng)域必不可少的一項關(guān)鍵技術(shù)。

那么QoS應(yīng)該怎么去做呢?下面還是結(jié)合交通的例子進(jìn)行介紹說明。

2.1 流量分類

從前面的圖我們看到不管是什么車,都以自我為中心,不受任何約束,我們首先能先到的辦法是對道路進(jìn)行分類劃分,比如分為公交車專用車道、小型車專用車道、大貨車專用車道、非機(jī)動車道以及人行橫道等,正常情況下公交車車道只允許公交車運(yùn)行,而非機(jī)動車道上是不允許出現(xiàn)機(jī)動車的,這樣我們可以保證車道與車道之間不受制約干擾。

如何設(shè)計并實現(xiàn)存儲QoS

同樣,存儲內(nèi)部也會有很多流量,我們可以為不同的流量類型分配不同的 “車道”,比如業(yè)務(wù)流量的車道我們劃分寬一些,而內(nèi)部壓縮流量的車道相對來說可以窄一些,由此引入了QoS中一個比較重要的概覽就是流量分類,根據(jù)分類結(jié)果可以進(jìn)行更加精準(zhǔn)個性化的限流控制。

2.2 流量優(yōu)先級

僅僅依靠分類是不行的,因為總有一些特殊情況,比如急救車救人、警車抓人等,我們總不能說這個車道只能跑普通私家小轎車把,一些特殊車輛(救護(hù)車,消防車以及警車等)應(yīng)該具有優(yōu)先通行的權(quán)限。

如何設(shè)計并實現(xiàn)存儲QoS

對于存儲來說業(yè)務(wù)流量就是我們的特殊車輛,我們需要保證業(yè)務(wù)流量的穩(wěn)定性,比如業(yè)務(wù)流量的帶寬跟IOPS不受限制,而內(nèi)部流量如遷移、修復(fù)則需要限定其帶寬或者IOPS,為其分配固定的“車道”。在資源充足的情況下,內(nèi)部流量可以安安靜靜的在自己的車道上行駛,但是當(dāng)資源緊張,比如業(yè)務(wù)流量突增或者持續(xù)性的高流量水位,這個時候需要限制內(nèi)部流量的道理寬度,極端情況下可以暫停。當(dāng)然,如果內(nèi)部流量都停了還是不能滿足正常業(yè)務(wù)流量的讀寫需求,這個時候就需要考慮擴(kuò)容的事情了。

QoS中另外一個比較重要的概念就是優(yōu)先級劃分,在資源充足的情況下執(zhí)行預(yù)分配資源策略,當(dāng)資源緊張時對優(yōu)先級低的服務(wù)資源進(jìn)行動態(tài)調(diào)整,進(jìn)行適當(dāng)?shù)囊?guī)避或者暫停,在一定程度上可以彌補(bǔ)預(yù)分配方案的不足。

2.3 流量監(jiān)控

前面提到當(dāng)資源不足時,我們可以動態(tài)的去調(diào)整其他流量的閾值,那我們?nèi)绾沃蕾Y源不足呢?這個時候我們是需要有個流量監(jiān)控的組件。

如何設(shè)計并實現(xiàn)存儲QoS

我們出行時經(jīng)常會使用地圖,通過選擇合適的線路以最快到達(dá)目的地。一般線路會通過不同的顏色標(biāo)記線路擁堵情況,比如紅色表示堵車、綠色表示暢通。

存儲想要知道機(jī)器或者磁盤當(dāng)前的流量情況有兩種方式:

  • 統(tǒng)計機(jī)器負(fù)載情況,比如我們經(jīng)常去機(jī)器上通過iostat命名查看各個磁盤的io情況,這種方式與機(jī)器上的應(yīng)用解耦,只關(guān)注機(jī)器本身

  • 統(tǒng)計各個應(yīng)用下發(fā)的讀寫流量,比如某臺機(jī)器上部署了一個存儲節(jié)點(diǎn)應(yīng)用,那我們可以統(tǒng)計這個應(yīng)用下發(fā)下去的讀寫帶寬及IOPS

第二種方式相對第一種可以實現(xiàn)應(yīng)用內(nèi)部更細(xì)的流量分類,比如前面提到的一個存儲應(yīng)用節(jié)點(diǎn),就包含了多種流量,我們不能通過機(jī)器的粒度對所有流量統(tǒng)一限流。

3. 常見QoS限流算法

3.1 固定窗口算法

  • 按時間劃分為多個限流窗口,比如1秒為一個限流窗口大小;

  • 每個窗口都有一個計數(shù)器,每通過一個請求計數(shù)器會加一;

  • 當(dāng)計數(shù)器大小超過了限制大小(比如一秒內(nèi)只能通過100個請求),則窗口內(nèi)的其他請求會被丟棄或排隊等待,等到下一個時間節(jié)點(diǎn)計數(shù)器清零再處理請求。

如何設(shè)計并實現(xiàn)存儲QoS

固定窗口算法的理想流量控制效果如上左側(cè)圖所示,假定設(shè)置1秒內(nèi)允許的最大請求數(shù)為100,那么1秒內(nèi)的最大請求數(shù)不會超過100。

但是大多數(shù)情況下我們會得到右側(cè)的曲線圖,即可能會出現(xiàn)流量翻倍的效果。比如前T1~T2時間段沒有請求,T2~T3來了100個請求,全部通過。下一個限流窗口計數(shù)器清零,然后T3T4時間內(nèi)來了100個請求,全部處理成功,這個時候時間段T4T5時間段就算有請求也是不能處理的,因此超過了設(shè)定閾值,最終T2~T4這一秒時間處理的請求為200個,所以流量翻倍。

小結(jié)

  • 算法易于理解,實現(xiàn)簡單;

  • 流量控制不夠精細(xì),容易出現(xiàn)流量翻倍情況;

  • 適合流量平緩并允許流量翻倍的模型。

3.2 滑動窗口算法

前面提到固定窗口算法容易出現(xiàn)流量控制不住的情況(流量翻倍),滑動窗口可以認(rèn)為是固定窗口的升級版本,可以規(guī)避固定窗口導(dǎo)致的流量翻倍問題。

  • 時間窗口被細(xì)分若干個小區(qū)間,比如之前一秒一個窗口(最大允許通過60個請求),現(xiàn)在一秒分成3個小區(qū)間,每個小區(qū)間最大允許通過20個請求;

  • 每個區(qū)間都有一個獨(dú)立的計數(shù)器,可以理解一個區(qū)間就是固定窗口算法中的一個限流窗口;

  • 當(dāng)一個區(qū)間的時間用完,滑動窗口往后移動一個分區(qū),老的分區(qū)(T1~T2)被丟棄,新的分區(qū)(T4~T5)加入滑動窗口,如圖所示。

如何設(shè)計并實現(xiàn)存儲QoS

小結(jié)

  • 流量控制更加精準(zhǔn),解決了固定窗口算法導(dǎo)致的流量翻倍問題;

  • 區(qū)間劃分粒度不易確定,粒度太小會增加計算資源,粒度太大又會導(dǎo)致整體流量曲線不夠平滑,使得系統(tǒng)負(fù)載忽高忽低;

  • 適合流量較為穩(wěn)定,沒有大量流量突增模型。

3.3 漏斗算法

  • 所有的水滴(請求)都會先經(jīng)過“漏斗”存儲起來(排隊等待);

  • 當(dāng)漏斗滿了之后,多余的水會被丟棄或者進(jìn)入一個等待隊列中;

  • 漏斗的另外一端會以一個固定的速率將水滴排出。

如何設(shè)計并實現(xiàn)存儲QoS

對于漏斗而言,他不清楚水滴(請求)什么時候會流入,但是總能保證出水的速度不會超過設(shè)定的閾值,請求總是以一個比較平滑的速度被處理,如圖所示,系統(tǒng)經(jīng)過漏斗算法限流之后,流量能保證在一個恒定的閾值之下。

小結(jié)

  • 穩(wěn)定的處理速度,可以達(dá)到整流的效果,主要對下游的系統(tǒng)起到保護(hù)作用;

  • 無法應(yīng)對流量突增情況,所有的請求經(jīng)過漏斗都會被削緩,因此不適合有流量突發(fā)的限流場景;

  • 適合沒有流量突增或想達(dá)到流量整合以固定速率處理的模型。

3.4 令牌桶算法

令牌桶算法是漏斗算法的一種改進(jìn),主要解決漏斗算法不能應(yīng)對流量突發(fā)的場景

  • 以固定的速率產(chǎn)生令牌并投入桶中,比如一秒投放N個令牌;

  • 令牌桶中的令牌數(shù)如果大于令牌桶大小M,則多余的令牌會被丟棄;

  • 所有請求到達(dá)時,會先從令牌桶中獲取令牌,拿到令牌則執(zhí)行請求,如果沒有獲取到令牌則請求會被丟棄或者排隊等待下一次嘗試獲取令牌。

如何設(shè)計并實現(xiàn)存儲QoS

如圖所示,假設(shè)令牌投放速率為100/s,桶能存放最大令牌數(shù)200,當(dāng)請求速度大于另外投放速率時,請求會被限制在100/s。如果某段時間沒有請求,這個時候令牌桶中的令牌數(shù)會慢慢增加直到200個,這是請求可以一次執(zhí)行200,即允許設(shè)定閾值內(nèi)的流量并發(fā)。

小結(jié)

  • 流量平滑;

  • 允許特定閾值內(nèi)的流量并發(fā);

  • 適合整流并允許一定程度流量突增的模型。

就單純的以算法而言,沒有哪個算法最好或者最差的說法,需要結(jié)合實際的流量特征以及系統(tǒng)需求等因素選擇最合適的算法。

四、存儲QoS設(shè)計及實現(xiàn)

4.1 需求

一般而言一臺機(jī)器會至少部署一個存儲節(jié)點(diǎn),節(jié)點(diǎn)負(fù)責(zé)多塊磁盤的讀寫請求,而存儲請求由分為多種類型,比如正常業(yè)務(wù)的讀寫流量、磁盤損壞的修復(fù)流量、數(shù)據(jù)刪除出現(xiàn)數(shù)據(jù)空洞后的空間壓縮流量以及多為了降低多副本存儲成本的糾刪碼(EC)遷移流量等等,不同流量出現(xiàn)在同一個存儲節(jié)點(diǎn)會相互競爭搶占系統(tǒng)資源,為了更好的保證業(yè)務(wù)服務(wù)質(zhì)量,需要對流量的帶寬以及IOPS進(jìn)行限制管控,比如需要滿足以下條件:

如何設(shè)計并實現(xiàn)存儲QoS

  • 可以同時限制流量的帶寬跟IOPS,單獨(dú)的帶寬或者IOPS限制都會導(dǎo)致另外一個參數(shù)不受控制而影響系統(tǒng)穩(wěn)定性,比如只控制了帶寬,但是沒有限制IOPS,對于大量小IO的場景就會導(dǎo)致機(jī)器的ioutil過高;

  • 可以實現(xiàn)磁盤粒度的限流,避免機(jī)器粒度限流導(dǎo)致磁盤流量過載,比如圖所示,ec流量限制節(jié)點(diǎn)的帶寬最大值為10Mbps,預(yù)期效果是想每塊磁盤分配2Mbps,但是很有可能這10Mbps全部分配到了第一個磁盤;

  • 可以支持流量分類控制,根據(jù)不同的流量特性設(shè)置不同的限流參數(shù),比如業(yè)務(wù)流量是我們需要重點(diǎn)保護(hù)的,因此不能對業(yè)務(wù)流量進(jìn)行限流,而EC、壓縮等其他流量均為內(nèi)部流量,可以根據(jù)其特性配置合適的限流閾值;

  • 可以支持限流閾值的動態(tài)適配,由于業(yè)務(wù)流量不能進(jìn)行流控,對于系統(tǒng)而言就像一匹“脫韁野馬”,可能突增、突減或持續(xù)高峰,針對突增或持續(xù)高峰的場景系統(tǒng)需要盡可能的為其分配資源,這就意味著需要對內(nèi)部流量的限流閾值進(jìn)行動態(tài)的打壓設(shè)置是暫停規(guī)避。

4.2 算法選擇

前面提到了QoS的算法有很多,這里我們結(jié)合實際需求選擇滑動窗口算法,主要有以下原因:

  • 系統(tǒng)需要控制內(nèi)部流量而內(nèi)部流量相對比較穩(wěn)定平緩;

  • 可以避免流量突發(fā)情況而影響業(yè)務(wù)流量;

QoS組件除了滑動窗口,還需要添加一個緩存隊列,當(dāng)請求被限流之后不能被丟棄,需要添加至緩存隊列中,等待下一個時間窗口執(zhí)行,如下圖所示。

如何設(shè)計并實現(xiàn)存儲QoS

4.3 帶寬與IOPS同時限制

為了實現(xiàn)帶寬與IOPS的同時控制,QoS組件將由兩部分組成:IOPS控制組件負(fù)責(zé)控制讀寫的IOPS,帶寬控制組件負(fù)責(zé)控制讀寫的帶寬,帶寬控制跟IOPS控制類似,比如帶寬限制閾值為1Mbps,那么表示一秒最多只能讀寫1048576Bytes大小數(shù)據(jù);假定IOPS限制為20iops,表示一秒內(nèi)最多只能發(fā)送20次讀寫請求,至于每次讀寫請求的大小并不關(guān)心。

如何設(shè)計并實現(xiàn)存儲QoS

兩個組件內(nèi)部相互隔離,整體來看又相互影響,比如當(dāng)IOPS控制很低時,對應(yīng)的帶寬可能也會較小,而當(dāng)帶寬控制很小時對應(yīng)的IOPS也會比較小。

如何設(shè)計并實現(xiàn)存儲QoS

下面以修復(fù)流量為例,分三組進(jìn)行測試

  1. 第一組:20iops-1Mbps

  2. 第二組:40iops-2Mbps

  3. 第三組:80iops-4Mbps

測試結(jié)果如上圖所示,從圖中可以看到qos模塊能控制流量的帶寬跟iops維持在設(shè)定閾值范圍內(nèi)。

4.4 流量分類限制

為了區(qū)分不同的流量,我們對流量進(jìn)行標(biāo)記分類,并為不同磁盤上的不同流量都初始化一個QoS組件,QoS組件之間相互獨(dú)立互不影響,最終可以達(dá)到磁盤粒度的帶寬跟IOPS控制。

如何設(shè)計并實現(xiàn)存儲QoS

4.5 動態(tài)閾值調(diào)整

前面提到的QoS限流方案,雖然能夠很好的控制內(nèi)部流量帶寬或者IOPS在閾值范圍內(nèi), 但是存在以下不足

  • 不感知業(yè)務(wù)流量現(xiàn)狀,當(dāng)業(yè)務(wù)流量突增或者持續(xù)高峰時,內(nèi)部流量與業(yè)務(wù)流量仍然會存在資源搶占,不能達(dá)到流量規(guī)避或暫停效果。

  • 磁盤上不同流量的限流相互獨(dú)立,當(dāng)磁盤的整體流量帶寬或者IOPS過載時,內(nèi)部流量閾值不能動態(tài)調(diào)低也會影響業(yè)務(wù)流量的服務(wù)質(zhì)量。

所以需要對QoS組件進(jìn)行一定的改進(jìn),增加流量監(jiān)控組件,監(jiān)控組件主要監(jiān)控不同流量類型的帶寬與IOPS,動態(tài)QoS限流方案支持以下功能:

如何設(shè)計并實現(xiàn)存儲QoS

  • 通過監(jiān)控組件獲取流量增長率,如果出現(xiàn)流量突增,則動態(tài)調(diào)低滑動窗口閾值以降低內(nèi)部流量;當(dāng)流量恢復(fù)平緩,恢復(fù)滑動窗口最初閾值以充分利用系統(tǒng)資源。

  • 通過監(jiān)控組件獲取磁盤整體流量,當(dāng)整體流量大小超過設(shè)定閾值,則動態(tài)調(diào)低滑動窗口大?。划?dāng)整體流量大小低于設(shè)定閾值,則恢復(fù)滑動窗口至初始閾值。

下面設(shè)置磁盤整體流量閾值2Mbps-40iops,ec流量的閾值為10Mbps-600iops

當(dāng)磁盤整體流量達(dá)到磁盤閾值時會動態(tài)調(diào)整其他內(nèi)部流量的閾值,從測試結(jié)果可以看到ec的流量受動態(tài)閾值調(diào)整存在一些波動,磁盤整體流量下去之后ec流量閾值又會恢復(fù)到最初閾值(10Mbps-600iops),但是可以看到整體磁盤的流量并沒有控制在2Mbps-40iops以下,而是在這個范圍上下波動,所以我們在初始化時需要保證設(shè)置的內(nèi)部流量閾值小于磁盤的整體流量閾值,這樣才能達(dá)到比較穩(wěn)定的內(nèi)部流量控制效果。

如何設(shè)計并實現(xiàn)存儲QoS

4.6 偽代碼實現(xiàn)

前面提到存儲QoS主要是限制讀寫的帶寬跟IOPS,具體應(yīng)該如何去實現(xiàn)呢?IO讀寫主要涉及以下幾個接口。

Read(p []byte) (n int, err error)ReadAt(p []byte, off int64) (n int, err error)Write(p []byte) (written int, err error)WriteAt(p []byte, off int64) (written int, err error)

所以這里需要對上面幾個接口進(jìn)行二次封裝,主要是加入限流組件。

帶寬控制組件實現(xiàn)

Read實現(xiàn)

// 假定c為限流組件func (self *bpsReader) Read(p []byte) (n int, err error) {
 size := len(p)  size = self.c.assign(size) //申請讀取文件大小
 n, err = self.underlying.Read(p[:size]) //根據(jù)申請大小讀取對應(yīng)大小數(shù)據(jù)  self.c.fill(size - n) //如果讀取的數(shù)據(jù)大小小于申請大小,將沒有用掉的計數(shù)填充至限流窗口中  return}

Read限流之后會出現(xiàn)以下情況

  • 讀取大小n<len(p)且err=nil,比如需要讀4K大小,但是當(dāng)前時間窗口只能允許讀取3K,這個是被允許的

這里也許你會想,Read限流的實現(xiàn)怎么不弄個循環(huán)呢?如直到讀取指定大小數(shù)據(jù)才返回。這里的實現(xiàn)我們需要參考標(biāo)準(zhǔn)的IO的讀接口定義,其中有說明在讀的過程中如果準(zhǔn)備好的數(shù)據(jù)不足len(p)大小,這里直接返回準(zhǔn)備好的數(shù)據(jù),而不是等待,也就是說標(biāo)準(zhǔn)的語義是支持只讀部分準(zhǔn)備好的數(shù)據(jù),因此這里的限流實現(xiàn)保持一致。

// Reader is the interface that wraps the basic Read method.//// Read reads up to len(p) bytes into p. It returns the number of bytes// read (0 <= n <= len(p)) and any error encountered. Even if Read// returns n < len(p), it may use all of p as scratch space during the call.// If some data is available but not len(p) bytes, Read conventionally// returns what is available instead of waiting for more.// 省略//// Implementations must not retain p.type Reader interface {  Read(p []byte) (n int, err error)}

ReadAt實現(xiàn)

下面介紹下ReadAt的實現(xiàn),從接口的定義來看,可能覺得ReadAt與Read相差不大,僅僅是指定了數(shù)據(jù)讀取的開始位置,細(xì)心的小伙伴可能發(fā)現(xiàn)我們這里實現(xiàn)時多了一層循環(huán),需要讀到指定大小數(shù)據(jù)或者出現(xiàn)錯誤才返回,相比Read而言ReadAt是不允許出現(xiàn)*n<len(p)且err==nil*的情況

func (self *bpsReaderAt) ReadAt(p []byte, off int64) (n int, err error) {  for n < len(p) && err == nil {    var nn int    nn, err = self.readAt(p[n:], off)    off += int64(nn)    n += nn  }  return}
func (self *bpsReaderAt) readAt(p []byte, off int64) (n int, err error) {  size := len(p)  size = self.c.assign(size)  n, err = self.underlying.ReadAt(p[:size], off)  self.c.fill(size - n)  return}
// ReaderAt is the interface that wraps the basic ReadAt method.//// ReadAt reads len(p) bytes into p starting at offset off in the// underlying input source. It returns the number of bytes// read (0 <= n <= len(p)) and any error encountered.//// When ReadAt returns n < len(p), it returns a non-nil error// explaining why more bytes were not returned. In this respect,// ReadAt is stricter than Read.//// Even if ReadAt returns n < len(p), it may use all of p as scratch// space during the call. If some data is available but not len(p) bytes,// ReadAt blocks until either all the data is available or an error occurs.// In this respect ReadAt is different from Read.//省略//// Implementations must not retain p.type ReaderAt interface {  ReadAt(p []byte, off int64) (n int, err error)}

Write實現(xiàn)

Write接口的實現(xiàn)相對比較簡單,循環(huán)寫直到寫完數(shù)據(jù)或者出現(xiàn)錯誤

func (self *bpsWriter) Write(p []byte) (written int, err error) {  size := 0  for size != len(p) {    p = p[size:]    size = self.c.assign(len(p))
   n, err := self.underlying.Write(p[:size])    self.c.fill(size - n)    written += n    if err != nil {      return written, err    }  }  return}
// Writer is the interface that wraps the basic Write method.//// Write writes len(p) bytes from p to the underlying data stream.// It returns the number of bytes written from p (0 <= n <= len(p))// and any error encountered that caused the write to stop early.// Write must return a non-nil error if it returns n < len(p).// Write must not modify the slice data, even temporarily.//// Implementations must not retain p.type Writer interface {  Write(p []byte) (n int, err error)}

WriteAt實現(xiàn)

這里的實現(xiàn)跟Write類似

func (self *bpsWriterAt) WriteAt(p []byte, off int64) (written int, err error) {  size := 0  for size != len(p) {    p = p[size:]    size = self.c.assign(len(p))
   n, err := self.underlying.WriteAt(p[:size], off)    self.c.fill(size - n)    off += int64(n)    written += n    if err != nil {      return written, err    }  }  return}
// WriterAt is the interface that wraps the basic WriteAt method.//// WriteAt writes len(p) bytes from p to the underlying data stream// at offset off. It returns the number of bytes written from p (0 <= n <= len(p))// and any error encountered that caused the write to stop early.// WriteAt must return a non-nil error if it returns n < len(p).//// If WriteAt is writing to a destination with a seek offset,// WriteAt should not affect nor be affected by the underlying// seek offset.//// Clients of WriteAt can execute parallel WriteAt calls on the same// destination if the ranges do not overlap.//// Implementations must not retain p.type WriterAt interface {  WriteAt(p []byte, off int64) (n int, err error)}

IOPS控制組件實現(xiàn)

IOPS控制組件的實現(xiàn)跟帶寬類似,這里就不詳細(xì)介紹了

Read接口實現(xiàn)
func (self *iopsReader) Read(p []byte) (n int, err error) {  self.c.assign(1) //這里只需要獲取一個計數(shù),如果當(dāng)前窗口一個都沒有,則會一直等待直到獲取到一個才喚醒執(zhí)行下一步  n, err = self.underlying.Read(p)  return}
ReadAt接口實現(xiàn)
func (self *iopsReaderAt) ReadAt(p []byte, off int64) (n int, err error) {  self.c.assign(1)  n, err = self.underlying.ReadAt(p, off)  return}

想想這里的ReadAt為啥不需要跟帶寬一樣循環(huán)讀了呢?


Write接口實現(xiàn)
func (self *iopsWriter) Write(p []byte) (written int, err error) {  self.c.assign(1)  written, err = self.underlying.Write(p)  return}

WriteAt

func (self *iopsWriterAt) WriteAt(p []byte, off int64) (n int, err error) {  self.c.assign(1)  n, err = self.underlying.WriteAt(p, off)  return}

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

qos
AI