很多時候我們如果不想等待緩存的過期,想要主動清除緩存,可以采用第三方的緩存清除模塊清除緩存nginx_ngx_cache_purge 。安裝nginx的時候,需要添加 purge 模塊, purge 模塊我們已經下載了,在 /usr/local/server 目錄下,添加該模塊 --add-module=/usr/local/server/ngx_cache_purge-2.3/ ,這一個步驟我們在安裝 OpenRestry 的時候已經實現了。
安裝好了后,我們配置一個清理緩存的地址:
這里 代表的是 (/.*) 的數據。
每次請求 $host$key 就可以刪除指定緩存,我們可以先查看緩存文件的可以:
此時訪問 <http://192.168.211.141/purge/user/wangwu>
此時再查看緩存文件,已經刪除了。
參數說明:
Lua是一門以其性能著稱的腳本語言,被廣泛應用在很多方面。Lua一般用于嵌入式應用,現在越來越多應用于游戲當中,魔獸世界,憤怒的小鳥都有用到。
優勢:
Lua腳本的作用:嵌入到應用程序中,給應用程序提供擴展功能。
依賴安裝: yum install libtermcap-devel ncurses-devel libevent-devel readline-devel pcre-devel gccopenssl openssl-devel
下載安裝包: curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz
解壓安裝: tar zxf lua-5.3.5.tar.gz
安裝:
此時原來系統自帶的lua其實是5.1.4,我們需要替換原來系統自帶的lua:
查看版本
創建hello.lua文件,內容為
編輯文件hello.lua
在文件中輸入:
保存并退出。
執行命令
輸出為:
效果如下:
lua有交互式編程(在控制臺編寫代碼)和腳本式編程(在文件中寫代碼叫腳本編程)。
交互式編程就是直接輸入語法,就能執行。
腳本式編程需要編寫腳本,然后再執行命令 執行腳本才可以。
一般采用腳本式編程。(例如:編寫一個hello.lua的文件,輸入文件內容,并執行lua hell.lua即可)
(1)交互式編程
Lua 提供了交互式編程模式。我們可以在命令行中輸入程序并立即查看效果。
Lua 交互式編程模式可以通過命令 lua -i 或 lua 來啟用:
如下圖:
(2)腳本式編程
我們可以將 Lua 程序代碼保持到一個以 lua 結尾的文件,并執行,該模式稱為腳本式編程,例如上面入門程序中將lua語法寫到hello.lua文件中。
注釋
一行注釋:兩個減號是單行注釋:
多行注釋
定義變量
全局變量,默認的情況下,定義一個變量都是全局變量,
如果要用局部變量 需要聲明為local.例如:
如果變量沒有初始化:則 它的值為nil 這和java中的null不同。
如下圖案例:
Lua中的數據類型
Lua 是動態類型語言,變量不要類型定義,只需要為變量賦值。 值可以存儲在變量中,作為參數傳遞或結果返回。
Lua 中有 8 個基本類型分別為:nil、boolean、number、string、userdata、function、thread 和 table。
lua紅有個type函數,能出對象的類型。
實例:
if語句
Lua if 語句 由一個布爾表達式作為條件判斷,其后緊跟其他語句組成。
語法:
實例:
if..else語句
Lua if 語句可以與 else 語句搭配使用, 在 if 條件表達式為 false 時執行 else 語句代碼塊。
語法:
實例:
循環[練習]
while循環[滿足條件就循環]
Lua 編程語言中 while 循環語句在判斷條件為 true 時會重復執行循環體語句。 語法:
實例:
效果如下:
for循環[練習]
Lua 編程語言中 for 循環語句可以重復執行指定語句,重復次數可在 for 語句中控制。
語法: 1->10 1:exp1 10:exp2 2:exp3:遞增的數量
var 從 exp1 變化到 exp2,每次變化以 exp3 為步長遞增 var,并執行一次 "執行體"。exp3 是可選的,如果不指定,默認為1。
例子:
for i=1,9,2 :i=1從1開始循環,9循環數據到9結束,2每次遞增2
repeat...until語句[滿足條件結束][練習]
Lua 編程語言中 repeat...until 循環語句不同于 for 和 while循環,for 和 while 循環的條件語句在當前循環執行開始時判斷,而 repeat...until 循環的條件語句在當前循環結束后判斷。
語法:
案例:
函數
lua中也可以定義函數,類似于java中的方法。例如:
執行之后的結果:
..:表示拼接
表
table 是 Lua 的一種數據結構用來幫助我們創建不同的數據類型,如:數組、字典等。
Lua也是通過table來解決模塊(module)、包(package)和對象(Object)的。
案例:
模塊
(1)模塊定義
模塊類似于一個封裝庫,從 Lua 5.1 開始,Lua 加入了標準的模塊管理機制,可以把一些公用的代碼放在一個文件里,以 API 接口的形式在其他地方調用,有利于代碼的重用和降低代碼耦合度。
創建一個文件叫module.lua,在module.lua中創建一個獨立的模塊,代碼如下:
由上可知,模塊的結構就是一個 table 的結構,因此可以像操作調用 table 里的元素那樣來操作調用模塊里的常量或函數。
上面的 func2 聲明為程序塊的局部變量,即表示一個私有函數,因此是不能從外部訪問模塊里的這個私有函數,必須通過模塊里的公有函數來調用.
(2)require 函數
require 用于 引入其他的模塊,類似于java中的類要引用別的類的效果。
用法:
兩種都可以。
我們可以將上面定義的module模塊引入使用,創建一個test_module.lua文件,代碼如下:
任何項目中我們都有一些頻繁的查詢,而那些頻繁的查詢數據基本都是相同的,比如項目中查看用戶個人信息,購物狂歡查詢活動信息,這些功能點使用頻率非常高,我們一般需要對他們做特殊處理。
我們以狂歡活動信息為例,當雙十一的時候,很多人會去查看今天優惠活動有哪些,對這些活動信息我們可以采用多級緩存架構來抗住高并發。
基于Java版的緩存架構實現流程如下:
優點:
缺點:
優點:
我們以雙十一活動信息加載為例,通過多級緩存架構來加載雙十一活動信息,雙十一活動信息表結構如下:
創建一個lua腳本 mysql.lua ,用于提供根據SQL語句執行查詢操作,代碼如下:
我們需要安裝 lua-resty-redis-cluster ,用該依賴庫實現Redis集群的操作,這里提供github地址,大家下載后按操作配置即可,下載地址: <https://github.com/cuiweixie/lua-resty-redis-cluster> ,下載該文件配置后即可實現Redis集群操作。
接下來我們實現Redis集群操作,創建一個腳本 redis.lua ,腳本中實現Redis中數據的查詢和添加操作,代碼如下:
配置創建Nginx緩存共享空間
創建多級緩存操作腳本 activity.lua ,代碼如下:
Nginx配置:
每次在雙十一或者618的時候,各大電商平臺為了吸引用戶,都會給與用戶很大的優惠,比如送優惠券、搶紅包等,搶優惠券或者紅包的用戶群體非常大,這時候會給服務器帶來的并發也是非常大,解決這塊問題,架構是關鍵。
雙十一搶紅包或者過年微信、QQ紅包雨活動的并發量非常龐大,紅包雨存在的規則也是非常多的,我們先來分析一下紅包雨的特點。
上面我們已經分析過紅包雨的特點,要想實現一套高效的紅包雨系統,緩存架構是關鍵。我們根據紅包雨的特點設計了如上圖所示的紅包雨緩存架構體系。
并發量非常大的系統,例如秒殺、搶紅包、搶票等操作,都是存在溢出現象,比如秒殺超賣、搶紅包超額、一票多單等溢出現象,如果采用數據庫鎖來控制溢出問題,效率非常低,在高并發場景下,很有可能直接導致數據庫崩潰,因此針對高并發場景下數據溢出解決方案我們可以采用Redis緩存提升效率。
用戶搶紅包的時候,我們會分批次發放紅包,每次發放紅包會先根據發放紅包的金額和紅包的個數把每個紅包計算好,然后存入到Redis隊列中,存入到Redis隊列后,用戶每次搶紅包都直接從Redis隊列中獲取一個紅包即可,由于Redis是單線程,在這里可以有效的避免多個人同時搶到了一個紅包,類似一種超賣現象。
表結構設計:
初始化讀取
創建容器監聽類 com.itheima.quartz.MoneyPushTask ,讓該類實現接口 ApplicationListener ,當容器初始化完成后,會調用 onApplicationEvent 方法, 我們可以在該方法中實現初始化讀取數據,將數據填充到變量中。
MoneyPushTask 代碼如下:
定時加載
定時加載我們需要開
搶紅包緩存隊列溢出控制
上面已經實現將紅包存入到Redis緩存隊列中,用戶每次搶紅包的時候,只需要從Redis緩存隊列中獲取即可。com.itheima.controller.RedPacketController 代碼:
com/itheima/service/RedPacketService.java 代碼:
com.itheima.service.impl.RedPacketServiceImpl 代碼:
用戶搶紅包的高并發場景下,如果讓后端服務器直接處理所有搶紅包操作,服務器很有可能會崩潰,就像高鐵站如果很多人蜂擁擠進站而不排隊,很有可能導致整個高鐵站秩序混亂,高鐵站服務崩潰,程序也是如此。
解決大量并發用戶蜂擁而上的方法可以采用隊列術將用戶的請求用隊列緩存起來,后端服務從隊列緩存中有序消費,可以防止后端服務同時面臨處理大量請求。緩存用戶請求可以用RabbitMQ、Kafka、RocketMQ、ActiveMQ,我們這里采用RabbitMQ即可。
用戶搶紅包的時候,我們用Lua腳本實現將用戶搶紅包的信息以生產者角色將消息發給RabbitMQ,后端應用服務以消費者身份從RabbitMQ獲取消息并搶紅包,再將搶紅包信息以WebSocket方式通知給用戶。
如果想使用Lua識別用戶令牌,我們需要引入 lua-resty-jwt 模塊,是用于 ngx_lua 和 LuaJIT 的 Lua 實現庫,在該模塊能實現Jwt令牌生成、Jwt令牌校驗,依賴庫的地址: https://github.com/SkyLothar/lua-resty-jwt
lua-resty-jwt安裝
在 資料\lua 中已經下載好了該依賴庫 lua-resty-jwt-master.zip ,我們將該庫文件上傳到服務器上,并解壓,當然,我們也可以使用opm直接安裝 lua-resty-jwt ,配置 lua-resty-jwt 之前,我們需要先安裝resty和opm。
安裝倉庫管理工具包:
添加倉庫地址:
安裝resty:
安裝opm:
安裝Jwt組件:
此時 lua-resty-jwt 安裝好了,可以直接使用了。
令牌校驗庫token.lua
搶紅包隊列腳本:mq.lua,參考地址 <https://github.com/wingify/lua-resty-rabbitmqstomp>
搶紅包隊列腳本:mq.lua,參考地址 <https://github.com/wingify/lua-resty-rabbitmqstomp>
Java監聽
打開資料中的 html/websocket.html ,執行搶紅包測試,效果如下:
一般情況下,雙十一很多查詢的并發量是比較大的,即使 有了多級緩存,當用戶不停的刷新頁面的時候,也是沒有必要的,另外如果有惡意的請求 大量達到,也會對系統造成影響。而限流就是保護措施之一。
限流和生活中很多現實場景類似,如下場景:
而程序限流,主要是控制應對高并發和惡意操作的一種技術手段。
nginx提供兩種限流的方式:
控制速率的方式之一就是采用漏桶算法。
(1)漏桶算法實現控制速率限流
漏桶(Leaky Bucket)算法思路很簡單,水(請求)先進入到漏桶里,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(訪問頻率超過接口響應速率),然后就拒絕請求,可以看出漏桶算法能強行限制數據的傳輸速率.示意圖如下:
(2)nginx的配置
配置示如下:
創建限流緩存空間:
配置限流:
參數說明:
如果惡意一直刷新會出現如下現象:
(3)處理突發流量
上面例子限制 2r/s,如果有時正常流量突然增大,超出的請求將被拒絕,無法處理突發流量,可以結合 burst 參數使用來解決該問題。
例如,如下配置表示:
burst 譯為突發、爆發,表示在超過設定的處理速率后能額外處理的請求數,當 rate=10r/s 時,將1s拆成10份,即每100ms可處理1個請求。
此處,burst=4 ,若同時有4個請求到達,Nginx 會處理第一個請求,剩余3個請求將放入隊列,然后每隔500ms從隊列中獲取一個請求進行處理。若請求數大于4,將拒絕處理多余的請求,直接返回503.
不過,單獨使用 burst 參數并不實用。假設 burst=50 ,rate依然為10r/s,排隊中的50個請求雖然每100ms會處理一個,但第50個請求卻需要等待 50 * 100ms即 5s,這么長的處理時間自然難以接受。
因此,burst 往往結合 nodelay 一起使用。
例如:如下配置:
如上表示:
平均每秒允許不超過2個請求,突發不超過4個請求,并且處理突發4個請求的時候,沒有延遲,等到完成之后,按照正常的速率處理。
完整配置如下:
ngx_http_limit_conn_module 提供了限制連接數的能力。主要是利用limit_conn_zone和limit_conn兩個指令。利用連接數限制 某一個用戶的ip連接的數量來控制流量。
注意:并非所有連接都被計算在內 只有當服務器正在處理請求并且已經讀取了整個請求頭時,才會計算有效連接。此處忽略測試。
配置語法:
(1)配置限制固定連接數
如下,配置如下:
配置限流緩存空間:
location配置:
參數說明:
(2)限制每個客戶端IP與服務器的連接數,同時限制與虛擬服務器的連接總數。
限流緩存空間配置:
location配置
每個IP限流 3個
總量5個
外!CFM-Hello語音杯團隊賽開戰,就在今天!16個民間大手子隊要開始神仙打架了!比賽看個熱鬧就完事兒了,冠軍那幾萬大獎跟咱沒關系;直播間搞得抽獎活動,我等屁民還是可以去爭一爭滴,聽說發的是現金喔!
Hello語音這次組織的比賽真是聲勢浩大,一個星期前250支隊伍打海選,打到現在剩16支隊伍爭冠;這16支隊伍的平民玩家不光過足了電競的癮,享受到比賽的刺激,還能馬上在斗魚直播里當一回明星選手,簡直不要太爽!
Hello語音為這次團隊賽請來了CFM官方解說西瓜丶SV,另外馬上開打的個人賽請到官方解說朗大朝;都是官方解說界的大紅人了,有這倆專業解說,觀賽體驗都上了一個檔次了,這民間賽的辦賽規格怕是跟官方專業比賽有一拼噢!
(團隊賽解說西瓜丶SV直播地址:https://www.douyu.com/5961790)
(個人賽解說朗大朝直播地址:https://www.douyu.com/6368510)
比賽相關的事簡單說下吧,4月8號16強賽正式開打,打到18號決冠軍,基本是晚上7點開打,具體的比賽信息hello語音里都有,要了解的朋友自己去看;這次冠軍獎金達到15000,總獎金也有40000,前8都有錢,搞得我都想組隊參加下一屆比賽了!
(團隊賽獎勵)
(官方地址:https://hstatic.ppx520.com/front/online/project/HYC-CFM/index.html)
另外,直播間有抽獎的事兒已經是板上釘釘了,聽說下現金紅包雨和貴重的游戲道具;我估摸著不光是OB間隙的常規抽獎,比如高光擊殺、選手采訪啥的也會來個突擊抽獎;所以各位觀眾老爺別怪我沒提醒,好好守著比賽時間吧,搞不好準點開播先來一手抽獎!
路人朋友的觀賽指南就這些了,下面聊聊圈內人的事兒,有興趣的朋友可以繼續閱讀。Hello語音這個比賽從海選到現在也打了一個星期了,講道理精彩程度還真不輸職業比賽;現金獎勵多的好處就是選手都特認真,而打出的高光操作都來自我們身邊的普通人,這種觀賽代入感遠比看職業比賽要強的多;說不定下一屆我們也能成為賽事的其中一員,去追逐屬于自己的電競夢!
隨著公司業務的不斷擴張,用戶流量在不斷提升,研發體系的規模和復雜性也隨之增加。線上服務的穩定性也越來越重要,服務性能問題,以及容量問題也越發明顯。
因此有必要搭建一個有效壓測系統,提供安全、高效、真實的線上全鏈路壓測服務,為線上服務保駕護航。
關于全鏈路壓測的建設,業界已經有了非常多文章,但是涉及到具體的技術實現方面,卻很少介紹。本文想從全鏈路壓測系統,從設計到落地整個實踐過程,來詳細介紹下全鏈路壓測系統是具體是如何設計,以及如何落地的。希望能從技術落地實踐的角度,給同行業的同學一些參考和啟發。
全鏈路壓測在業內已經有了廣泛的實踐,如阿里的 Amazon、PTS[1][2],美團的 Quake[3][4],京東的的 ForceBOT[5],高德的 TestPG[6]等等,都為我們提供豐富的實踐經驗,和大量優秀的技術方案。我們廣泛吸收了各大互聯網公司的全鏈路壓測建設經驗,并基于字節跳動業務需求,設計開發了一個全鏈路壓測系統 Rhino。
Rhino 平臺作為公司級的全鏈路壓測平臺,它的目標是對全公司所有業務,提供單服務、全鏈路,安全可靠、真實、高效的壓測,來幫助業務高效便捷的完成性能測試任務,更精確評估線上服務性能&容量方面風險。
因此在 Rhino 平臺設計之初,我們就定下以下目標:
Rhino 是一個分布式全鏈路壓測系統,可以通過水平擴展,來實現模擬海量用戶真實的業務操作場景,對線上各種業務進行全方位的性能測試。它主要分為控制中心(Rhino Master)模塊,壓測鏈路服務模塊,監控系統模塊,壓測引擎模塊,如圖。(每一個模塊都是由多個微服務來完成的。如下圖每個實線圖都代表一個微服務或多個微服務)。
壓測過程中數據構造是最重要,也是最為復雜的環節。壓測數據的建模,直接影響了壓測結果的準確性。
為了高效的構造特定的 Fake 壓測數據,Rhino 壓測平臺提供大量數據構造方式:
在壓測過程中,有些壓測請求需要進行登錄,并保持會話;此外在很多壓測請求中涉及到用戶賬號信息 UserID,DeviceID 等數據。用戶賬號的構造問題,一直是壓測過程中非常棘手的問題。Rhino 平臺打通的用戶中心,設置了壓測專屬的賬號服務,完美地解決了壓測過程中的登錄態,以及測試賬號等問題。具體流程和使用界面,如下圖。
壓測隔離中需要解決的壓測流量隔離,以及壓測數據的隔離。
壓測流量隔離,主要是通過構建壓測環境來解決,如線下壓測環境,或泳道化/Set 化建設,將壓測流量與線上流程完全隔離。優點是壓測流量與線上流量完全隔離,不會影響到線上用戶。缺點:機器資源及維護成本高,且壓測結果需要經過一定的換算,才能得線上容量,結果準確性存在一定的問題。目前公司內壓測都是在線上集群上完成的,線上泳道化正在建設中。
壓測數據隔離,主要是通過對壓測流量進行染色,讓線上服務能識別哪些是壓測流量,哪些是正常流量,然后對壓測流量進行特殊處理,以達到數據隔離的目的。目前 Rhino 平臺整體壓測隔離框架如圖。
壓測標記就是最常見的壓測流量染色的方式。
目前公司內各個基礎組件、存儲組件,以及 RPC 框架都已經支持了壓測標記的透傳。其原理是將壓測標記的 KV 值存入 Context 中,然后在所有下游請求中都帶上該 Context,下游服務可以根據 Context 中壓測標記完成對壓測流量的處理。在實際業務中,代碼改造也非常簡單,只需要透傳 Context 即可。
Golang 服務: 將壓測標記寫入 Context 中。
Python 服務:利用 threading.local()存儲線程 Context。
Java 服務:利用 ThreadLocal 存儲線程 Context。
為了解決線上壓測安全問題,我們還引入了壓測開關組件。
線上壓測中,最復雜的問題就是壓測鏈路中涉及到寫操作,如何避免污染線上數據,并且能保證壓測請求保持和線上相同的請求路徑。業界有很多解決方案,常見的有影子表,影子庫,以及數據偏移,如圖[7]。
Rhino 平臺針對不同存儲,有不同的解決方案:
在壓測之前,需要對服務進行壓測驗證。對于不滿足壓測要求(即壓測數據隔離)的服務,需要進行壓測改造。
a. 盡量減少代碼改動,并給出完整的指導手冊及代碼示例,減少 RD 的工作量,降低代碼錯誤的可能性
b. 提供簡單便捷的線上線下 HTTP&RPC 的壓測請求 Debug 工具,方便代碼改動的驗證
c. 對于新項目,在項目開始初期,就將壓測改造加入項目開發規范中,減少后期的代碼改動
請求調用鏈,對于線上壓測是非常重要的:
Rhino 平臺通過公司的流式日志系統來完成調用鏈檢索的。一個服務在被請求或者請求下游時,都會透傳一個 LogID。RPC 框架會打印調用鏈日志(包括 RPC 日志-調用者日志,Access 日志-被調用者日志),所有日志中都會包含這個 LogID。通過 LogID 將一個請求所經過的所有服務日志串起來,就完成調用鏈檢索。
Rhino 平臺在公司流式日志系統提供的鏈路梳理功能基礎上,進行了進一步優化,以滿足壓測需要:
雖然 Rhino 平臺對于壓測有很多的安全保障措施,但是對于大型壓測,保證信息的通暢流通也是非常重要的。因此在壓測周知方面,Rhino 平臺也提供了很多解決方案:
在壓測之前,需要開啟整體鏈路的壓測開關的,否則壓測流量就會被服務拒絕,導致壓測失敗。
對于調用鏈中不能壓測的服務(敏感服務),或者第三方服務,為了壓測請求的完整性,就需要對這些服務進行 Mock。業界通用的 Mock 方案有:
由于字節整個公司都采用微服務架構,導致一次壓測涉及鏈路都比較長,快速無業務入侵的 Mock
方式成為了首選。Rhino 平臺是通過公司 Service Mesh 和 ByteMock 系統來實現了高效的,對業務透明的服務 Mock。
壓測執行前,Rhino 平臺需要向 Service Mesh 注冊染色轉發規則,并向 Mock 服務注冊 Mock 規則。然后在壓測流量中注入 Mock 染色標記,才能完成服務 Mock:
Rhino 平臺中,壓測 Agent 就是一個最小調度單元。一次壓測任務,通常會拆分成多個子 Job,然后下發到多個 Agent 上來完成。
2020 年春節搶紅包壓測中,Rhino 臨時擴容在 4000+個實例,支撐了單次 3kw+QPS 的壓測,但日常 Rhino 平臺只部署了 100+個實例,就能滿足日常壓測需求。
Rhino 平臺默認將全鏈路壓測分為公網壓測和內網壓測。公網壓測主要 IDC 網絡帶寬,延時,IDC 網關新建連接、轉發等能力;內網壓測,主要是壓測目標服務,目標集群的 性能,容量等。
Rhino 平臺在各個 IDC 都有部署 Agent 集群。各個 IDC 內服務的壓測,默認會就近選擇壓測 Agent,來減少網絡延時對壓測結果的干擾,使得壓測結果更精準,壓測問題定位更簡單。
除了多機房部署之外,Rhino 平臺還在邊緣計算節點上也部署了壓測 Agent,來模擬各種不同地域不同運營商的流量請求,確保流量來源,流量分布更貼近真實情況。在 Rhino 平臺上可以選擇不同地域不同運營商,從全國各個地區發起壓測流量。
為了應對線上壓測風險,Rhino 平臺提供兩種熔斷方式,來應對壓測過程中的突發事件,來降低對線上服務造成的影響。
每個壓測任務,都可以關聯調用鏈中任意服務的告警規則。在壓測任務執行過程,Rhino 平臺會主動監聽告警服務。 當調用鏈中有服務出現了告警,會立即停止壓測。對于沒有關聯的告警,Rhino 平臺也會記錄下來,便于壓測問題定位。
自定義監控指標及閾值,到達閾值后,也會自動停止壓測。目前支持 CPU、Memory、 上游穩定性、錯誤日志,以及其他自定義指標。
此外,除了 Rhino 平臺自身提供的熔斷機制以外,公司服務治理架構也提供了很多額外的熔斷機制,如壓測開關,一鍵切斷壓測流量;過載保護,服務過載時自動丟棄壓測流量。
對于 HTTP 協議,參考了 Postman,全部可視化操作,保證所有人都能上手操作,極大降低了壓測的使用門檻和成本。
對于 RPC 任務,Rhino 也自動完成了對 IDL 的解析,然后轉換成 JSON 格式,便于用戶參數化處理。
對于非 HTTP/RPC 的協議,以及有復雜邏輯的壓測任務,Rhino 平臺也提供了完善的解決方案——Go Plugin。
Go Plugin 提供了一種方式,通過在主程序和共享庫直接定義一系列的約定或者接口,就可以動態加載其他人編譯的 Go 語言共享對象,使得主程序可以在編譯后動態加載共享庫,實現熱插拔的插件系統。此外主程序和共享庫的開發者不需要共享代碼,只要雙方的約定不變,修改共享庫后也不再需要重新編譯主程序。
用戶只要根據規范要求,實現一段發壓業務邏輯代碼即可。Rhino 平臺可以自動拉取代碼,觸發編譯。并將編譯后的插件 SO 文件分發到多個壓測 Agent。 Agent 動態加載 SO 文件,并發運行起來,就可以達到壓測的目的。此外,Rhino 還針對常見 Go Plugin 壓測場景,建立了壓測代碼示例代碼庫。對于壓測新手,簡單修改下業務邏輯代碼,就可以完成壓測了。這樣就解決了非常見協議,以及復雜壓測場景等的壓測問題。
壓測調度的最小單元是壓測 Agent,但是實際每個 Agent 中有掛載多種壓測引擎的,來支撐不同的壓測場景。Rhino 平臺在壓測數據和壓測引擎之間增加了一個壓測引擎適配層,實現了壓測數據與壓測引擎的解耦。壓測引擎適配層,會根據選擇不同的壓測引擎,生成不同 Schema 的壓測數據,啟用不同的引擎來完成壓測,而這些對用戶是透明的。
在壓測引擎上,我們有開源的壓測引擎,也有自研的壓測引擎。
開源壓測引擎的優點是維護人多,功能比較豐富,穩定且性能好,缺點就是輸入格式固定,定制難度大。此外 Agent 與開源壓測引擎之間通常是不同進程,進程通信也存在比較大的問題,不容易控制。
自研壓測引擎,優點是和 Agent 通常運行在單進程內,比較容易控制;缺點可能就是性能稍微差一些。但是 Golang 天然支持高并發,因此自研和開源之間的性能差距并不明顯。
由于公司監控系統,最小時間粒度是 30s,30s 內的數據會聚合成一個點。這個時間粒度對于壓測來說是比較難以接受的。因此,Rhino 平臺自己搭建了一套客戶端監控系統。
服務端監控,直接接入了公司 Metric 系統。
在壓測過程中,Rhino 平臺還可以實時采集目標服務進程的性能 Profile,并通過火焰圖的方式展示出來,方便用戶進行性能問題分析和優化,如圖。
Rhino 壓測平臺是一個面向全字節跳動公司的,為了所有研發同學提供的一站式全鏈路壓測的平臺。Rhino 平臺的研發團隊,不僅負責 Rhino 平臺的研發任務,還會配合 QA&RD 來完成公司大型項目,重點業務的性能壓測工作。
公司內重大項目的壓測,Rhino 平臺都會積極參與,全力支撐的。其中,比較典型的項目有抖音春晚,西瓜百萬英雄,春節紅包雨等活動。
其中字節春節紅包雨活動,完成是由 Rhino 團隊來負責和完成的。字節春節紅包雨活動是在春節期間,所有字節客戶端發起的,諸如抽卡分現金,紅包錦鯉,紅包雨等一系列的超大規模的紅包引流活動。其流量規模巨大,流量突發性強,業務邏輯和網絡架構復雜度高等等,都對 Rhino 平臺提出不小的挑戰。
在春節紅包雨活動中,所有用戶流量都經過運營商專線接入到網絡邊緣的匯聚機房,然后經過過濾和驗證后,再轉發到核心機房。其中各個 IDC 互為備份,其具體流量路線如圖。在這里,不僅要驗證后端各服務是否能承載預期流程,還要驗證各個專線帶寬,各個網關帶寬及轉發能力,各 IDC 承載能力以及之間帶寬等等。
為此,我們將整個壓測拆分成多個階段,來簡化壓測復雜性,也降低壓測問題定位的難度:
在這些大型項目的支撐中,Rhino 團隊不僅學到了大量的業務和架構設計知識,還了解到業務研發同學如何看待壓測,如何使用平臺,幫助我們發現更多平臺的問題,促進平臺不斷迭代優化。
日常壓測支撐,也是 Rhino 平臺非常重要的一項任務。對于日常壓測中遇到的各種問題,我們采用了各種方案來解決:
Rhino 平臺還實現了線上流量的定期調度,以達到線上實例自動壓測的目的[8]:
其具體實現方案如下:
目前已經有 500+微服務接入,每天定時執行流量調度,來監控線上服務性能變化趨勢,如下圖。
Rhino 平臺目前還在公司內推行常態化壓測,通過周期定時化的自動化全鏈路壓測,來實現以下目標:
目前 Rhino 平臺上的常態化壓測,會周期定時,以無人值守的方式,自動執行壓測任務,并推送壓測結果。在壓測執行過程中,會根據調用鏈自動完成壓測開關開啟,發起壓測流量。實時監控服務性能指標,并根據 Metric 及告警監控,自動完成壓測熔斷,以保證壓測安全。
目前已經有多個業務方接入常態化壓測,以此保證線上服務的穩定性。
服務在上線時,都會經過預發布,線上小流量灰度,線上全量發布。在這個過程中,我們可以通過線上測試 Case 以及灰度發布,來攔截服務線上功能缺陷。但是對于性能缺陷的攔截,卻不夠有效。
從線上故障跟蹤系統里就可以發現,由于上線前沒有做好性能壓測,很多性能缺陷都逃逸到了線上。
為了攔截各種性能缺陷,Rhino 平臺完成了 DevOps 平臺的打通。將壓測服務在 DevOps 平臺上注冊成一個原子服務 ,研發人員可以將壓測節點編排在任意流水線的任意位置,實現上線前的例行壓測。DevOps 流水線中的壓測,不僅可以幫助 RD 發現代碼中的性能問題,還能與性能基線進行 Diff,來發現代碼性能變壞的味道。
Rhino 壓測平臺從立項到現在,不到兩年的時間內,其發展已經初具規模,如圖(每月壓測執行統計)。這個期間,非常非常感謝公司內所有合作團隊,尤其是架構團隊,中臺團隊對壓測平臺的支撐,沒有他們的支撐,全鏈路壓測建設是難以完成的。
通用壓測平臺已經初步搭建完成,基本上能滿足業務線日常壓測需求。但在日常壓測支撐過程中,發現不同業務線在壓測時,但是仍然有大量的前置和后繼工作需要人工來完成。
如何更進一步降低業務方壓測改造的成本,如何減少壓測環境數據預置成本,如何快速完成壓測數據清理,如何快速定位出性能問題等等,Rhino 壓測平臺后續將更進一步深入業務,與各大業務方開展更深入的合作,提供更深度的業務定制,為研發提效,助力業務線發展。
業務目前資源是否充足,其具體容量是多少;按照目前業務增長,其機器資源還能支撐多久?
目前服務資源利用如何,是否可以優化,如何更進一步提升資源利用率,降低機器資源成本?
某大型活動,需要申請多少資源?是否不需要壓測,或者自動化利用線上流量數據,或者利用日常壓測數據,就可以給出上述問題的結論?
如何保證服務穩定性,如何監控服務性能劣化并及時預警,限流、超時、重試以及熔斷等服務治理措施配置是否合理?以及如何配合混沌測試進行容災演練,保證服務穩定性等等,這些 Rhino 平臺都會做更進一步探索。
目前 Rhino 團隊還非常小,非常缺少性能測試以及后端開發相關的研發工程師,歡迎感興趣的同學來加入。簡歷投遞郵箱: tech@bytedance.com ;郵件標題: 姓名 - 工作年限 - Rhino 。
[1] http://jm.taobao.org/2017/03/30/20170330/
[2] https://testerhome.com/topics/19493
[3] https://tech.meituan.com/2018/09/27/quake-introduction.html
[4] https://tech.meituan.com/2019/02/14/full-link-pressure-test-automation.html
[5] https://www.open-open.com/lib/view/open1484317425690.html
[6] https://www.infoq.cn/article/NvfJekpvU154pwlsCTLW
[7] https://tech.bytedance.net/articles/3199
[8] https://www.usenix.org/conference/osdi16/technical-sessions/presentation/veeraraghavan
Fastbot:行進中的智能 Monkey
品質優化 - 圖文詳情頁秒開實踐
Android Camera 內存問題剖析
字節跳動自研線上引流回放系統的架構演進
歡迎關注「字節跳動技術團隊」
*請認真填寫需求信息,我們會在24小時內與您取得聯系。