限流算法目前程序開發(fā)過程常用的限流算法有兩個:漏桶算法和令牌桶算法。
溫泉網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián),溫泉網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為溫泉數(shù)千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請找那個售后服務(wù)好的溫泉做網(wǎng)站的公司定做!
漏桶算法
漏桶算法的原理比較簡單,請求進入到漏桶中,漏桶以一定的速率漏水。當請求過多時,水直接溢出。可以看出,漏桶算法可以強制限制數(shù)據(jù)的傳輸速度。如圖所示,把請求比作是水滴,水先滴到桶里,通過漏洞并以限定的速度出水,當水來得過猛而出水不夠快時就會導(dǎo)致水直接溢出,即拒絕服務(wù)。
圖片來自網(wǎng)絡(luò)
漏桶的出水速度是恒定的,那么意味著如果瞬時大流量的話,將有大部分請求被丟棄掉(也就是所謂的溢出)。
令牌桶算法
令牌桶算法的原理是系統(tǒng)以一定速率向桶中放入令牌,如果有請求時,請求會從桶中取出令牌,如果能取到令牌,則可以繼續(xù)完成請求,否則等待或者拒絕服務(wù)。這種算法可以應(yīng)對突發(fā)程度的請求,因此比漏桶算法好。
圖片來自網(wǎng)絡(luò)
漏桶算法和令牌桶算法的選擇
兩者的主要區(qū)別漏桶算法能夠強行限制處理數(shù)據(jù)的速率,不論系統(tǒng)是否空閑。而令牌桶算法能夠在限制數(shù)據(jù)的平均處理速率的同時還允許某種程度的突發(fā)流量。如何理解上面的含義呢?漏桶算法,比如系統(tǒng)吞吐量是 120/s,業(yè)務(wù)請求 130/s,使用漏斗限流 100/s,起到限流的作用,多余的請求將產(chǎn)生等待或者丟棄。對于令牌桶算法,每秒產(chǎn)生 100 個令牌,系統(tǒng)容量 200 個令牌。正常情況下,業(yè)務(wù)請求 100/s 時,請求能被正常被處理。當有突發(fā)流量過來比如 200 個請求時,因為系統(tǒng)容量有 200 個令牌可以同一時刻處理掉這 200 個請求。如果是漏桶算法,則只能處理 100 個請求,其他的請求等待或者被丟棄。
其實最簡單的方法是用timer控件,timer控件本事就是對一個線程的封裝
所以你用兩個timer控件就可以模擬兩個線程了
或者用兩個backgroundworker控件,這個更逼真,不用定時觸發(fā)
具體用法,我空間里有教程
數(shù)據(jù)庫有自己的連接鎖機制,如果是針對同一臺機器使用同一個接口進行插入的話多線程和單線程是一樣的。除非你有好幾臺數(shù)據(jù)庫服務(wù)器,這樣再使用多線程來進行上面的工作的話效率才會明顯提高。
使用mysql異步查詢,需要使用mysqlnd作為PHP的MySQL數(shù)據(jù)庫驅(qū)動。 使用MySQL異步... 如果創(chuàng)建的線程過多,則會造成線程切換引起系統(tǒng)負載過高。Swoole中的異步MySQL其...
在MySQL 8.0 之前, 我們假設(shè)一下有一條爛SQL,
mysqlselect * from t1 order by rand() ;
以多個線程在跑,導(dǎo)致CPU被跑滿了,其他的請求只能被阻塞進不來。那這種情況怎么辦?
大概有以下幾種解決辦法:
設(shè)置max_execution_time 來阻止太長的讀SQL。那可能存在的問題是會把所有長SQL都給KILL 掉。有些必須要執(zhí)行很長時間的也會被誤殺。
自己寫個腳本檢測這類語句,比如order by rand(), 超過一定時間用Kill query thread_id 給殺掉。
那能不能不要殺掉而讓他正常運行,但是又不影響其他的請求呢?
那mysql 8.0 引入的資源組(resource group,后面簡寫微RG)可以基本上解決這類問題。
比如我可以用 RG 來在SQL層面給他限制在特定的一個CPU核上,這樣我就不管他,讓他繼續(xù)運行,如果有新的此類語句,讓他排隊好了。
為什么說基本呢?目前只能綁定CPU資源,其他的暫時不行。
那我來演示下如何使用RG。
創(chuàng)建一個資源組user_ytt. 這里解釋下各個參數(shù)的含義,
type = user 表示這是一個用戶態(tài)線程,也就是前臺的請求線程。如果type=system,表示后臺線程,用來限制mysql自己的線程,比如Innodb purge thread,innodb read thread等等。
vcpu 代表cpu的邏輯核數(shù),這里0-1代表前兩個核被綁定到這個RG??梢杂胠scpu,top等列出自己的CPU相關(guān)信息。
thread_priority 設(shè)置優(yōu)先級。user 級優(yōu)先級設(shè)置大于0。
mysqlmysql create resource group user_ytt type = user ?vcpu = 0-1 thread_priority=19 enable;Query OK, 0 rows affected (0.03 sec)
RG相關(guān)信息可以從 information_schema.resource_groups 系統(tǒng)表里檢索。
mysqlmysql select * from information_schema.resource_groups;+---------------------+---------------------+------------------------+----------+-----------------+| RESOURCE_GROUP_NAME | RESOURCE_GROUP_TYPE | RESOURCE_GROUP_ENABLED | VCPU_IDS | THREAD_PRIORITY |+---------------------+---------------------+------------------------+----------+-----------------+| USR_default ? ? ? ? | USER ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ?1 | 0-3 ? ? ?| ? ? ? ? ? ? ? 0 || SYS_default ? ? ? ? | SYSTEM ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ?1 | 0-3 ? ? ?| ? ? ? ? ? ? ? 0 || user_ytt ? ? ? ? ? ?| USER ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ?1 | 0-1 ? ? ?| ? ? ? ? ? ? ?19 |+---------------------+---------------------+------------------------+----------+-----------------+3 rows in set (0.00 sec)
我們來給語句select guid from t1 group by left(guid,8) order by rand() 賦予RG user_ytt。
mysql show processlist;+-----+-----------------+-----------+------+---------+-------+------------------------+-----------------------------------------------------------+| Id ?| User ? ? ? ? ? ?| Host ? ? ?| db ? | Command | Time ?| State ? ? ? ? ? ? ? ? ?| Info ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|+-----+-----------------+-----------+------+---------+-------+------------------------+-----------------------------------------------------------+| ? 4 | event_scheduler | localhost | NULL | Daemon ?| 10179 | Waiting on empty queue | NULL ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|| 240 | root ? ? ? ? ? ?| localhost | ytt ?| Query ? | ? 101 | Creating sort index ? ?| select guid from t1 group by left(guid,8) order by rand() || 245 | root ? ? ? ? ? ?| localhost | ytt ?| Query ? | ? ? 0 | starting ? ? ? ? ? ? ? | show processlist ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|+-----+-----------------+-----------+------+---------+-------+------------------------+-----------------------------------------------------------+3 rows in set (0.00 sec)
找到連接240對應(yīng)的thread_id。
mysqlmysql select thread_id from performance_schema.threads where processlist_id = 240;+-----------+| thread_id |+-----------+| ? ? ? 278 |+-----------+1 row in set (0.00 sec)
給這個線程278賦予RG user_ytt。沒報錯就算成功了。
mysqlmysql set resource group user_ytt for 278;Query OK, 0 rows affected (0.00 sec)
當然這個是在運維層面來做的,我們也可以在開發(fā)層面結(jié)合 MYSQL HINT 來單獨給這個語句賦予RG。比如:
mysqlmysql select /*+ resource_group(user_ytt) */guid from t1 group by left(guid,8) order by rand()....8388602 rows in set (4 min 46.09 sec)
RG的限制:
Linux 平臺上需要開啟 CAPSYSNICE 特性。比如我機器上用systemd 給mysql 服務(wù)加上
systemctl edit mysql@80 [Service]AmbientCapabilities=CAP_SYS_NICE
mysql 線程池開啟后RG失效。
freebsd,solaris 平臺thread_priority 失效。
目前只能綁定CPU,不能綁定其他資源。
現(xiàn)象
Sysbench對MySQL進行壓測, 并發(fā)數(shù)過大(5k)時, Sysbench建立連接的步驟會超時.
猜想
猜想: 直覺上這很簡單, Sysbench每建立一個連接, 都要消耗一個線程, 資源消耗過大導(dǎo)致超時.
驗證: 修改Sysbench源碼, 調(diào)大超時時間, 仍然會發(fā)生超時.
檢查環(huán)境
猜想失敗, 回到常規(guī)的環(huán)境檢查:
MySQL error log 未見異常.
syslog 未見異常.
tcpdump 觀察網(wǎng)絡(luò)包未見異常, 連接能完成正常的三次握手; 只觀察到在出問題的連接中, 有一部分的TCP握手的第一個SYN包發(fā)生了重傳, 另一部分沒有發(fā)生重傳.
自己寫一個簡單的并發(fā)發(fā)生器, 替換sysbench, 可重現(xiàn)場景. 排除sysbench的影響
猜想2
懷疑 MySQL 在應(yīng)用層因為某種原因, 沒有發(fā)送握手包, 比如卡在某一個流程上:
檢查MySQL堆棧未見異常, 仿佛MySQL在應(yīng)用層沒有看到新連接進入.
通過strace檢查MySQL, 發(fā)現(xiàn)?accept()?調(diào)用確實沒有感知到新連接.
懷疑是OS的原因, Google之, 得到參考文檔:?A TCP “stuck” connection mystery【】
分析
參考文檔中的現(xiàn)象跟目前的狀況很類似, 簡述如下:
正常的TCP連接流程:
Client 向 Server 發(fā)起連接請求, 發(fā)送SYN.
Server 預(yù)留連接資源, 向 Client 回復(fù)SYN-ACK.
Client 向 Server 回復(fù)ACK.
Server 收到 ACK, 連接建立.
在業(yè)務(wù)層上, Client和Server間進行通訊.
當發(fā)生類似SYN-flood的現(xiàn)象時, TCP連接的流程會使用SYN-cookie, 變?yōu)?
Client 向 Server 發(fā)起連接請求, 發(fā)送SYN.
Server 不預(yù)留連接資源, 向 Client 回復(fù)SYN-ACK, 包中附帶有簽名A.
Client 向 Server 回復(fù)ACK, 附帶 f(簽名A) (對簽名進行運算的結(jié)果).
Server 驗證簽名, 分配連接資源, 連接建立.
在業(yè)務(wù)層上, Client和Server間進行通訊.
當啟用SYN-cookie時, 第3步的ACK包因為?某種原因?丟失, 那么:
從Client的視角, 連接已經(jīng)建立.
從Server的視角, 連接并不存在, 既沒有建立, 也沒有”即將建立” (若不啟用SYN-cookie, Server會知道某個連接”即將建立”)
發(fā)生這種情況時:
若業(yè)務(wù)層的第一個包應(yīng)是從 Client 發(fā)往 Server, 則會進行重發(fā)或拋出連接錯誤
若業(yè)務(wù)層的第一個包應(yīng)是從 Server 發(fā)往 Client的, Server不會發(fā)出第一個包. MySQL的故障就屬于這種情況.
TCP握手的第三步ACK包為什么丟失
參考文檔中, 對于TCP握手的第三步ACK包的丟失原因, 描述為:
Some of these packets get lost because some buffer somewhere overflows.
我們可以通過Systemtap進一步探究原因.?通過一個簡單的腳本:
probe kernel.function("cookie_v4_check").return
{
source_port = @cast($skb-head + $skb-transport_header, "struct tcphdr")-source
printf("source=%d, return=%d\n",readable_port(source_port), $return)
}
function readable_port(port) {
return (port ((19)-1)) 8 | (port 8)
}
觀察結(jié)果, 可以確認cookie_v4_check?(syn cookie機制進行包簽名檢查的函數(shù))會返回 NULL(0). 即驗證是由于syn cookie驗證不通過, 導(dǎo)致TCP握手的第三步ACK包不被接受.
之后就是對其中不同條件進行觀察, 看看是哪個條件不通過. 最終原因是accept隊列滿(sk_acceptq_is_full):
static inline bool sk_acceptq_is_full(const struct sock ?*sk){ ? return sk-sk_ack_backlog sk- ? sk_max_ack_backlog;}
恢復(fù)故障與日志的正關(guān)聯(lián)
在故障處理的一開始, 我們就檢查了syslog, 結(jié)論是未見異常.
當整個故障分析完成, 得知了故障與syn cookie有關(guān), 回頭看syslog, 里面是有相關(guān)的信息, 只是和故障發(fā)生的時間不匹配, 沒有正關(guān)聯(lián), 因此被忽略.
檢查Linux源碼:
if (!queue-synflood_warned
sysctl_tcp_syncookies != 2
xchg(queue-synflood_warned, 1) == 0)
pr_info("%s: Possible SYN flooding on port %d. %s.
Check SNMP counters.\n",
proto, ntohs(tcp_hdr(skb)-dest), msg);
可以看到日志受到了抑制, 因此日志與故障的正關(guān)聯(lián)被破壞.
粗看源碼, 每個listen socket只會發(fā)送一次告警日志, 要獲得日志與故障的正關(guān)聯(lián), 必須每次測試重啟MySQL.
解決方案
這種故障一旦形成, 難以檢測; 系統(tǒng)日志中只會出現(xiàn)一次, 在下次重啟MySQL之前就不會再出現(xiàn)了; Client如果沒有合適的超時機制, 萬劫不復(fù).
解決方案:
1. 修改MySQL的協(xié)議, 讓Client先發(fā)握手包. 顯然不現(xiàn)實.
2. 關(guān)閉syn_cookie. 有安全的人又要跳出來了.
3. 或者調(diào)高syn_cookie的觸發(fā)條件 (syn backlog長度). 降低系統(tǒng)對syn flood的敏感度, 使之可以容忍業(yè)務(wù)的syn波動.
有多個系統(tǒng)參數(shù)混合影響syn backlog長度, 參看【】
下圖為精華總結(jié)
請點擊輸入圖片描述