先介紹下該測試框架
基本架構
基本功能
當要在同一臺主機上進行多條測試用例的並行時,對於CPU和內存的消耗比較大,因此,當需要模擬數以千計的併發用戶時,使用單臺機器模擬所有的併發用戶就有些力不從心,甚至會引起內存溢出錯誤。爲了讓該測試框架提供更大的負載能力,有了使用多臺機器同時產生負載的機制。
該使用測試框架時,測試人員可自行開發自動化測試用例,編寫完成後,上傳至我的服務端進行審覈,審覈通過後,會下發到與服務端連接的多臺slave從機,用戶可通過網頁客戶端,對用例進行分配,在多臺slave從機上進行測試。
那麼,是如何實現多臺負載機同時運行的呢?當然不會多個人坐在多臺負載機面前,一喊開始,大家同時啓動用例。這種方式很笨,也很難達到真正的同步。其實,我們通過單個服務端就可以控制多個遠程的slave從機,使它們同步的對服務器進行壓力測試或多用例並行測試。
通過遠程調用測試框架,測試人員可以跨越多臺低端計算機複製測試,這樣就可以模擬一個比較大的服務器壓力,一個服務端端實例,理論上可以控制任意多的遠程slave實例,並通過他們收集測試數據。這樣一樣,就有了如下特性:
* 保存測試採樣數據到本地機器
* 通過單臺機器管理多個執行引擎。
* 沒有必要將測試計劃複製到每一臺機器,服務端會將它發往每一臺slave從機。
* 每一臺slave從機都執行相同的測試計劃,服務端不會在執行期間做負載均衡,每一臺slave從機都會完整地運行測試計劃。
在1.4G Hz~3GHz 的CPU 、1GB 內存的 slave從機上,可以處理線程 100~300。但是Web Service 例外。XML處理是 CPU 運算密集的,會迅速消耗掉所有的CPU 。一般來說,以XML技術爲核心的應用系統,其性能將是普通Web 應用的 10%~25% 。另外,如果所有負載由一臺機器產生,網卡和交換機端口都可能產生瓶頸,所以一個slave從機線程數不應超過 10 0 。
採用遠程模式並不會比獨立運行相同數目的非GUI 測試更耗費資源。
使用多臺機器產生負載的操作步驟如下:
(1)在所有期望運行slave的負載生成器的機器上運行slave腳本。然後運行所有slave 機器上的server_client.py 文件
以下是server_client.py部分代碼
import socket import threading import os import json from ClientLog import for_test,for_test1,for_test2 from ClientLog import Send sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('192.168.1.102', 5550)) sock.listen(5) print('Server', socket.gethostbyname('192.168.1.102'), 'listening ...') mydict = dict() mylist = list() def subThreadIn(myconnection, connNumber): data = myconnection.recv(1024).decode('utf-8') print("data: " + data) client_data = json.loads(data) print("client_data:" + client_data['nickName'] + " user_id" + str(client_data['user_id'])) #nickname = myconnection.recv(1024).decode() nickname = client_data['nickName'] user_id = client_data['user_id'] mydict[myconnection.fileno()] = nickname mylist.append(myconnection) print('connection', connNumber, ' has nickname :', nickname) tellOthers(connNumber, '【系統提示:'+mydict[connNumber]) while True: try: recvedMsg = myconnection.recv(1024).decode() if recvedMsg: print(mydict[connNumber], ':', recvedMsg) if recvedMsg == "for_test": for_test.for_test(user_id,recvedMsg) Send.send(user_id,"準備測試: " + "for_test") elif recvedMsg == "for_test1": for_test1.for_test(user_id,recvedMsg) Send.send(user_id,"準備測試: " + "for_test1") elif recvedMsg == "for_test2": for_test2.for_test(user_id,recvedMsg) Send.send(user_id,"準備測試: " + "for_test2") tellOthers(connNumber, mydict[connNumber]+' :'+recvedMsg) except (OSError, ConnectionResetError) as e: Send.send(user_id,"測試結束: " + str(e)) try: mylist.remove(myconnection) except: pass print(mydict[connNumber], 'exit, ', len(mylist), ' person left') tellOthers(connNumber, '【系統提示:'+mydict[connNumber]') myconnection.close() return
(2)測試人員通過網頁規劃測試用例,服務端發送測試計劃到slave從機執行測試
以下是服務端核心代碼:
zhujidict = dict() zhujidict["主機1"] = "192.168.1.102" #爲了方便測試框架,選用的是同一臺slave從機 zhujidict["主機2"] = "192.168.1.102" zhujidict["主機3"] = "192.168.1.102" def send_case(request): user = request.user if not user.is_authenticated(): logger.error( u'[CommentView]當前用戶非活動用戶:[{}]'.format(user.username) ) request.session['LoginForm'] = request.META.get('HTTP_REFERER', '/') return render(request,'users/login.html') testcase = request.POST.getlist("test_case") zhuji = request.POST.getlist("zhuji") times = int(request.POST["times"]) Update.objects.create( text =user.username + ' : 測試用例 '+ str(testcase) + ' 測試主機: '+ str(zhuji), user = user, ) for i in range(times): for case_1 in testcase: for zhuji_1 in zhuji: zhuji_ip = zhujidict[zhuji_1] try: sendjson(case_1,zhuji_ip,user.id) except Exception as e: Update.objects.create( text =user.username + ' : 測試被迫中止,異常: ' + str(e), user = user, ) Update.objects.create( text =user.username + ' : 這是第 ' + str(i+1) + ' 次執行測試', user = user, ) return redirect('update') def sendjson(taskName,zhuji,user_id): try: data = { 'nickName':'SERVER_TASK', 'user_id':user_id, } json_str = json.dumps(data) print("JSON對象:",json_str) sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect((zhuji,5550)) sock.send(b'1') print(sock.recv(1024).decode()) sock.send(json_str.encode('utf-8')) time.sleep(1) sock.send(taskName.encode()) sock.close() except (OSError,ConnectionResetError): print(OSError)