Running Locust in Step Load Mode
If you want to monitor your service performance with different user load and probe the max tps that can be achieved, you can run Locust with Step Load enabled with --step-load:
如果你想分階段遞增並發用戶數壓測,找到系統最大的QPS,你可以用--step-load --step-users XX --step-time XX命令,不用再手動去增加用戶數和壓測時間。
譬如:
locust -f D:\api_locust\fm_api\locust_api\XXX.py --master --master-bind-port 9800 --headless -u 800 -r 50 --expect-worker 10 -t 50m -s 10 --step-load --step-users 200 --step-time 10m --csv D:\locustlog\
總共發起800並發請求,每秒啟動50個用戶,當啟動到200用戶數時,持續運行10分鍾,運行步驟大致如下:
(1)4s啟動到200
(2)200用戶數:10分鍾
(3)4s啟動到400
(4)400用戶數:10分鍾
(5)4s啟動到600
(6)600用戶數:10分鍾
(7)4s啟動到800
(8)800用戶數:10分鍾
(9)800用戶數:50-40=10分鍾(小於10分鍾)
壓測結果如圖:
可以得出系統qps最大也就550左右,后邊隨着並發數提升沒有提升,唯一提升是平均響應時間。
通過 --step-load --step-users這樣的命令就不用再折騰弄命令了。
關於壓測結果圖的生成,也很簡單的,上面的壓測命令會把壓測的歷史記錄寫到csv文件里,
讀取csv文件再生成報表即可:
python用到的模塊pyecharts
pyecharts文檔地址:http://pyecharts.org/#/zh-cn/intro
貼個代碼示例:
1 # -*- coding = utf-8 -*- 2 # ------------------------------ 3 # @time: 2020/8/15 16:05 4 # @Author: drew_gg 5 # @File: deal_csv.py 6 # @Software: api_locust 7 # ------------------------------ 8 9 import os 10 import time 11 import csv 12 import datetime 13 from pyecharts.charts import Line 14 from pyecharts import options as opts 15 from pyecharts.globals import ThemeType 16 17 """ 18 # pyecharts使用文檔 19 # http://pyecharts.org/#/zh-cn/intro 20 """ 21 22 pl = os.getcwd().split('api_locust') 23 path_html = pl[0] + 'api_locust\\resource\\html\\' 24 25 26 def report(interface_name, history_csv, stats_csv): 27 """ 28 locust生成報表 29 :param interface_name: 30 :param history_csv: 31 :param stats_csv: 32 :return: 33 """ 34 tm = [] 35 uc = [] 36 qps = [] 37 fps = [] 38 avg_time = [] 39 all_r = '' 40 all_qps = '' 41 all_avg_time = '' 42 with open(history_csv, 'r') as f: 43 hc = csv.reader(f) 44 for x, i in enumerate(list(hc)): 45 if i: 46 if x != 0: 47 tm.append(time.strftime('%H:%M:%S', time.localtime(int(i[0])))) 48 uc.append(str(round(float(i[1]), 2))) 49 qps.append(str(round(float(i[4]), 2))) 50 fps.append(str(round(float(i[5]), 2))) 51 avg_time.append(str(round(float(i[20]), 2))) 52 with open(stats_csv, 'r') as f: 53 sc = csv.reader(f) 54 for x, i in enumerate(list(sc)): 55 if x != 0 and len(i) != 0: 56 all_r = str(i[2]) 57 all_qps = str(round(float(i[9]), 2)) 58 all_avg_time = str(round(float(i[5]), 2)) 59 locust_title = "[{0}]壓測情況:[ARC:{1}, QPS: {2}, AVT: {3}]".format(interface_name, all_r, all_qps, all_avg_time) 60 locust_html_name = path_html + interface_name + str(datetime.datetime.now().strftime('%m%d%H%M%S')) + '.html' 61 line = ( 62 Line(init_opts=opts.InitOpts(theme=ThemeType.CHALK, width="1800px", height="800px")) 63 .add_xaxis(tm) 64 .add_yaxis('user', uc, is_smooth=True) 65 .add_yaxis('qps', qps, is_smooth=True) 66 .add_yaxis('fps', fps, is_smooth=True) 67 .add_yaxis('avg_t', avg_time, is_smooth=True) 68 .set_global_opts( 69 title_opts=opts.TitleOpts(title=locust_title), 70 tooltip_opts=opts.TooltipOpts(trigger="axis"), 71 toolbox_opts=opts.ToolboxOpts(is_show=True, orient="vertical", pos_left="1%", pos_top="10%"), 72 xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False,), 73 ) 74 ) 75 line.render(locust_html_name) 76 77 78 if __name__ == "__main__": 79 stats = "D:\\api_locust\\resource\\csv\\locust_exhibition_home.py0826095139_stats.csv" 80 history = "D:\\api_locust\\resource\\csv\\locust_exhibition_home.py0826095139_stats_history.csv" 81 report('a', history, stats)
關於報表的生成,不懂的自行去文檔地址仔細研究研究,挺全的,各種報表樣式都有。
另外,如果是分布式壓測的話,不要手動去啟動命令,自己寫個py去自動創建生成多好:
1 # -*- coding = utf-8 -*- 2 # ------------------------------ 3 # @time: 2020/8/15 16:05 4 # @Author: drew_gg 5 # @File: locust_main.py 6 # @Software: api_locust 7 # ------------------------------ 8 9 import datetime 10 import subprocess 11 12 13 class CoverLocust: 14 """ 15 封面壓測封裝類 16 """ 17 def __init__(self, ltp): 18 """ 19 初始化壓測參數 20 :param ltp: 參數字典 21 """ 22 self.user = ltp['user'] 23 self.r = ltp['r'] 24 self.slave = ltp['slave'] 25 self.m_time = ltp['m_time'] 26 self.port = ltp['port'] 27 self.master_host = ltp['master_host'] 28 self.csv = ltp['csv'] 29 self.master_script = ltp['master_script'] 30 self.slave_script = ltp['slave_script'] 31 self.step_u = ltp['step_u'] 32 self.step_t = ltp['step_t'] 33 # 定義csv文件名稱 34 self.csv_name = ltp["master_script"].split('\\')[-1] + str(datetime.datetime.now().strftime('%m%d%H%M%S')) 35 self.csv_path = ltp['csv'] + self.csv_name 36 # 輸出壓測結果目錄文件 37 self.stats_history_csv = self.csv_path + '_stats_history.csv' 38 self.stats_csv = self.csv_path + '_stats.csv' 39 self.failures_csv = self.csv_path + '_failures.csv' 40 41 # 生成master命令 42 def locust_master(self): 43 """ 44 運行master指令 45 :return: 46 """ 47 master_cmd = "locust -f %s --master --master-bind-port %s --headless " % (self.master_script, self.port) 48 master_pra = "-u %s -r %s --expect-worker %s -t %s -s 10 --step-load --step-users %s --step-time %s --csv %s "\ 49 % (self.user, self.r, self.slave, self.m_time, self.step_u, self.step_t, self.csv_path) 50 master_cmd = master_cmd + master_pra 51 print(master_cmd) 52 subprocess.Popen(master_cmd, creationflags=subprocess.CREATE_NEW_CONSOLE) 53 return self.stats_history_csv, self.stats_csv, self.failures_csv, self.csv_name 54 55 # 生成slave命令 56 def locust_slave(self, slave_num): 57 """ 58 運行slave指令 59 :param slave_num: 60 :return: 61 """ 62 slave_cmd = "locust -f %s --master-host %s --master-port %s --headless --worker" % (self.slave_script, self.master_host, self.port) 63 print(slave_cmd) 64 for i in range(slave_num): 65 subprocess.Popen(slave_cmd, creationflags=subprocess.CREATE_NEW_CONSOLE) 66 67 # 殺掉壓測進程 68 @classmethod 69 def close_process(cls): 70 """ 71 殺掉命令進程 72 :return: 73 """ 74 import os 75 os.system('taskkill /IM locust.exe /F')
1 # -*- coding = utf-8 -*- 2 # ------------------------------ 3 # @time: 2020/8/18 20:36 4 # @Author: drew_gg 5 # @File: locust_run.py 6 # @Software: api_locust 7 # ------------------------------ 8 9 import os 10 import time 11 from run_locust import locust_main as locust_m 12 from locust_common.common_excel import deal_csv as report 13 14 pl = os.getcwd().split('api_locust') 15 path_csv = pl[0] + 'api_locust\\resource\\csv\\' 16 path_script = pl[0] + 'api_locust\\locust_view\\kbh_api\\locust_api\\' 17 script_name = "locust_exhibition_home.py" 18 19 # ************************************需維護更改的參數****************************# 20 # 並發數 21 user = 1400 22 # 每秒啟動用戶數 23 rate = 200 24 # 分布式壓測啟動的slave數量 25 num_slave = 10 26 # 本次壓測執行的時長:h,m,s 27 run_time = '25m' 28 # master所在host/ip 29 master_host = "10.111.53.123" 30 # master 執行腳本 31 master_script = path_script + script_name 32 # slave 執行腳本 33 slave_script = path_script + script_name 34 # # csv存儲文件的目錄 35 # csv_path = path_csv + script_name.split('.')[0] + '_log\\' 36 # 遞壓用戶數 37 step_user = 400 38 # 遞壓持續時間 39 step_time = '5m' 40 # ************************************需維護更改的參數****************************# 41 42 43 locust_p = { 44 'user': user, 45 "r": rate, 46 "slave": num_slave, 47 "m_time": run_time, 48 "port": 9800, 49 "master_host": master_host, 50 "csv": path_csv, 51 "master_script": master_script, 52 "slave_script": slave_script, 53 "step_u": step_user, 54 "step_t": step_time 55 } 56 57 if __name__ == "__main__": 58 59 # *****************執行腳本標識*******************# 60 sign = "master" 61 # sign = "slave" 62 # *****************執行腳本標識*******************# 63 lm = locust_m.CoverLocust(locust_p) 64 lm.close_process() 65 if sign == "master": 66 # 啟動master 67 history_csv, stats_csv, failures_csv, csv_name = lm.locust_master() 68 sleep_time = int(run_time[:-1]) 69 # 延遲30s讀取csv文件 70 if run_time[-1] == 'h': 71 sleep_time = sleep_time * 60 * 60 + 30 72 if run_time[-1] == 'm': 73 sleep_time = sleep_time * 60 + 30 74 if run_time[-1] == 's': 75 sleep_time = sleep_time + 30 76 time.sleep(sleep_time) 77 # 生成報表 78 report.report(script_name.split('.')[0], history_csv, stats_csv) 79 if sign == "slave": 80 # 啟動slave 分多台跑的話需要用num_slave除以台數 81 slave_num = int(num_slave/2) 82 lm.locust_slave(slave_num)
這樣要修改啥,直接修改就行,也可以自己寫個頁面,點一下按鈕就可以壓測。
上面的這個壓測結果圖:
這個說明QPS還有升高的空間,目前的並發數還沒到達瓶頸,還得再提升並發數【平均響應時間沒有急劇提升】。當然,這個地方設置的between(0,0.5),
所以會出現800的並發結果qps達到960以上。