點擊鏈接加入QQ群 522720170(免費公開課咪橙、視頻應(yīng)有盡有):https://jq.qq.com/?_wv=1027&k=5C08ATe
最近的一個項目野来,視頻點播系統(tǒng),使用apache實現(xiàn)的流媒體服務(wù)器。其實準(zhǔn)確的說叫做偽流(HTTP Pseudo-Streaming),基本原理和概念自行了解吧祟滴。
讓我簡單的描述就是仍然是HTTP下載,播放器播放本地緩存歌溉。只不過它也實現(xiàn)了一些和正規(guī)流媒體服務(wù)類似的功能垄懂。視頻點播類系統(tǒng)的性能測試,說簡單了主要就是模擬大量用戶去看視頻痛垛。服務(wù)端監(jiān)控整體性能草慧,客戶端關(guān)注各自表現(xiàn)。這里難點主要在于如何模擬大量用戶觀看視頻匙头?如何判斷各客戶端展現(xiàn)漫谷?
多客戶端的模擬無外乎3種方法:
1.全公司總動員,一起看乾胶。
2.一臺機器開多個播放頁面(或者是一個頁面上嵌入多個播放器)抖剿,調(diào)用多臺機器朽寞。
3.拋棄客戶端的解碼播放過程识窿,直接想法下載視頻文件。
第一種方法不用說脑融,雖然最真實喻频,但太原始沒有技術(shù)含量,如果是小公司人還不一定夠肘迎。
第二種方法在需要測試的用戶數(shù)不大時還可以甥温,否則會占用較多資源,而且技術(shù)含量也較低妓布。這種方式姻蚓,一臺機器上能模擬的用戶數(shù)很有限,因為CPU會很快成為瓶頸匣沼。
第三種方法最高效狰挡,在千兆網(wǎng)環(huán)境下,一臺機器可以模擬很多路用戶,此時網(wǎng)絡(luò)帶寬是瓶頸(如果帶寬更大加叁,估計硬盤IO就是瓶頸了)倦沧。但是沒有解碼和播放,如何判斷客戶端的效果呢它匕?兩種方式展融,一是對比下載速度和碼率,理論上只要下載速度足夠就可以了豫柬;二是測試過程中也可以打開幾個真實的播放窗口來驗證播放效果告希,因為壓力在服務(wù)端,可以認為每個客戶端都是等價的烧给。
本文要講的就是第三種測試方法暂雹。
以前還測過FMS的流媒體,網(wǎng)上可以找到現(xiàn)成的工具來實現(xiàn)本方法创夜,只要調(diào)用就可以了杭跪。但HTTP的這次也搜了兩天,好像還真沒有驰吓,只好自己動手了涧尿。看來最高效的方法檬贰,實現(xiàn)起來卻是最麻煩的姑廉。基本思路是JAVA實現(xiàn)下載功能翁涤,用LR的JAVA Vuser實現(xiàn)多用戶的控制桥言。動手前先給自己整理了一個需求
功能:
模擬下載√
播放列表√
下載速度記錄:總平均速度、每秒速度√
*偽裝成播放器進行連接
*客戶端限速
測試結(jié)果:
apache限速
下載速度
文件完整
功能擴展
jar包或者exe文件傳參(播放列表)
打?qū)︺^的是已經(jīng)實現(xiàn)了葵礼,帶星號的是未實現(xiàn)号阿、也不一定需要實現(xiàn)的高級功能。通過測試程序鸳粉,需要得到的結(jié)果是下載是否正確完成扔涧?下載速度是否足夠?apache的限速模塊是否起作用(如果開啟)届谈?這里可能會有個問題枯夜,如果下載程序突破了apache的限速,那有可能是測試程序的問題艰山,這就引出了上面的高級功能“偽裝成播放器”湖雹。
下面直接貼代碼,說明盡量放到注釋里曙搬。
下載類摔吏,提供靜態(tài)方法汤踏。
package com.test;
import java.io.*;
import java.net.*;
import java.util.*;
public class DownloadFile {
? ? /*
? ? *下載指定URL的文件
? ? *userid是用于保存本地文件的標(biāo)識
? ? **/
? ? public static int getHttpFileByUrl(String address, String userid) {
? ? ? ? int bufferSize = 1024;
? ? ? ? int size = 0;
? ? ? ? byte[] buf = new byte[bufferSize];
? ? ? ? /*
? ? ? ? *下載時間字符串
? ? ? ? *用于文件標(biāo)識
? ? ? ? *格式201210260130
? ? ? ? **/? ? ? ?
? ? ? ? Date date = new Date();
? ? ? ? SimpleDateFormat formatDate = new SimpleDateFormat("yyyyMMddHHmmss");
? ? ? ? String downloadTime = formatDate.format(date);
? ? ? ? /*
? ? ? ? ? *計算下載速度相關(guān)
? ? ? ? ? */
? ? ? ? int totalDownloadSize = 0;
? ? ? ? int lastDownloadSize = 0;
? ? ? ? long lastCheckTime = 0;
? ? ? ? long startDownloadTime = 0;
? ? ? ? int sec = 0;
? ? ? ? /*
? ? ? ? *下載和計算過程
? ? ? ? */
? ? ? ? try {
? ? ? ? ? ? URL url = new URL(address);
? ? ? ? ? ? URLConnection conn = url.openConnection();
? ? ? ? ? ? BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());
? ? ? ? ? ? FileOutputStream fos = new FileOutputStream("e:\test\testvideo" + "_" + downloadTime + "_" + userid);
? ? ? ? ? ? System.out.println("File Size:" + conn.getContentLength()/1024 + "KB");
? ? ? ? ? ? startDownloadTime = System.currentTimeMillis();
? ? ? ? ? ? lastCheckTime = startDownloadTime;
? ? ? ? ? ? while ((size = bis.read(buf)) != -1)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? fos.write(buf,0,size);
? ? ? ? ? ? ? ? totalDownloadSize += size;
? ? ? ? ? ? ? ? /*
? ? ? ? ? ? ? ? *計算每秒下載速度
? ? ? ? ? ? ? ? */
? ? ? ? ? ? ? ? if (System.currentTimeMillis() - lastCheckTime > 1000) {
? ? ? ? ? ? ? ? ? ? System.out.println(sec + ": " + (totalDownloadSize - lastDownloadSize)/1024 + "KB");
? ? ? ? ? ? ? ? ? ? lastCheckTime = System.currentTimeMillis();
? ? ? ? ? ? ? ? ? ? lastDownloadSize = totalDownloadSize;
? ? ? ? ? ? ? ? ? ? sec++;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? System.out.println("Vuser " + userid + " Download Completed!");
? ? ? ? ? ? System.out.println("Average Download Speed: " + (totalDownloadSize/1024)/((System.currentTimeMillis() - startDownloadTime)/1000) + "KB/s");
? ? ? ? ? ? fos.close();
? ? ? ? ? ? bis.close();
? ? ? ? }catch (MalformedURLException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return totalDownloadSize/1024;
? ? }? ?
}
測試驅(qū)動。
import com.test.*;
import java.util.*;
import java.io.*;
public class DownloadDrive {
? ? public static void main (String[] args) {
? ? ? ? //System.out.println("test");
? ? ? ? //DownloadFile.test();
? ? ? ? /*
? ? ? ? *調(diào)用者測量
? ? ? ? *自行實現(xiàn)
? ? ? ? */
? ? ? ? int DownLoadSize = 0;
? ? ? ? double DownLoadTime = 0;
? ? ? ? int Speed = 0;
? ? ? ? String url;
? ? ? ? ArrayList urlList = new ArrayList();
? ? ? ? /*
? ? ? ? *讀取待下載的URL地址
? ? ? ? *保存到list中
? ? ? ? */
? ? ? ? try{
? ? ? ? ? ? BufferedReader br = new BufferedReader(new FileReader("url.txt"));
? ? ? ? ? ? while((url = br.readLine()) != null){
? ? ? ? ? ? ? ? urlList.add(url);
? ? ? ? ? ? }? ?
? ? ? ? }catch(IOException ie){
? ? ? ? ? ? ie.printStackTrace();
? ? ? ? }
? ? ? ? System.out.println("Total URLs: " + urlList.size());
? ? ? ? /*
? ? ? ? *依次下載list中的文件
? ? ? ? */
? ? ? ? for(int i = 0; i < urlList.size(); i++){
? ? ? ? ? ? url = (String)urlList.get(i);
? ? ? ? ? ? System.out.println(url);? ? ? ? ? ?
? ? ? ? ? ? //傳入url和每個調(diào)用者的標(biāo)識
? ? ? ? ? ? DownloadFile.getHttpFileByUrl(url, "1");
? ? ? ? }
? ? }? ?
}
將待下載的URL保存到url.txt中舔腾,每行一個地址溪胶。文件放到DownloadDrive.class同目錄中。
到這稳诚,主要代碼已經(jīng)實現(xiàn)了哗脖。下一步需要做的是和LR整合,用LR的Java Vuser對下載功能進行控制扳还,模擬多用戶才避。其實也就是重寫一個測試驅(qū)動,只不過這個驅(qū)動是需要LR內(nèi)部方法了氨距,應(yīng)該沒有什么技術(shù)上的難點了桑逝。
接下來,通過LoadRunner來實現(xiàn)多線程的模擬和控制俏让。
新建LR的JAVA Vuser腳本楞遏,這里可以直接進行JAVA編碼,又可以調(diào)用LR的內(nèi)部方法首昔,如事務(wù)寡喝、思考時間、集合點等等勒奇。到了這步已經(jīng)沒有任何難點了预鬓,開發(fā)人員只要花1個小時了解下LR的基本使用和常用方法即可,測試人員如果不會JAVA……那還是算了吧赊颠。
Action.java內(nèi)容如下:
import lrapi.lr;
import com.test.*;
import java.util.*;
import java.io.*;
public class Actions
{
? ? public int init() throws Throwable {
? ? ? ? return 0;
? ? }//end of init
? ? public int action() throws Throwable {
? ? ? ? /*
? ? ? ? 調(diào)用者測量
? ? ? ? */
? ? ? ? int downloadSize = 0;
? ? ? ? int downloadTime = 0;
? ? ? ? long startTime = 0;
? ? ? ? long endTime = 0;
? ? ? ? int speed = 0;
? ? ? ? int vid;
? ? ? ? vid = lr.get_vuser_id();
? ? ? ? /*
? ? ? ? 從url文件生成arraylist
? ? ? ? ? ? */
? ? ? ? String url;
? ? ? ? ArrayList urlList = new ArrayList();
? ? ? ? try{? ? ? ?
? ? ? ? ? ? ? ? BufferedReader br = new BufferedReader(new FileReader("url.txt"));
? ? ? ? while((url = br.readLine()) != null){
? ? ? ? ? ? urlList.add(url);
? ? ? ? }
? ? ? ? }catch(IOException ie){
? ? ? ? ie.printStackTrace();
? ? ? ? }
? ? ? ? lr.enable_redirection(true);
? ? ? ? lr.set_debug_message(lr.MSG_CLASS_JIT_LOG_ON_ERROR, lr.SWITCH_OFF);
? ? ? ? System.out.println("Total URLs: " + urlList.size());
? ? ? ? for(int i = 0; i < urlList.size(); i++){
? ? ? ? url = (String)urlList.get(i);
? ? ? ? System.out.println(url);
? ? ? ? //事務(wù)名稱
? ? ? ? String trxName = "URL" + (i+1);
? ? ? ? startTime = System.currentTimeMillis();
? ? ? ? lr.start_transaction(trxName);
? ? ? ? //傳入url和每個調(diào)用者的標(biāo)識
? ? ? ? downloadSize = DownloadFile.getHttpFileByUrl(url, Integer.toString(vid));
? ? ? ? lr.end_transaction(trxName, lr.AUTO);
? ? ? ? endTime = System.currentTimeMillis();
? ? ? ? downloadTime = (int)(endTime - startTime)/1000;
? ? ? ? speed = downloadSize / downloadTime;
? ? ? ? lr.output_message(trxName + ": completed");
? ? ? ? lr.output_message("time cost: " + downloadTime + "s");
? ? ? ? lr.output_message("average speed: " + speed + "KB/s");
? ? ? ? lr.output_message("");
? ? ? ? }
? ? ? ? lr.set_debug_message(lr.MSG_CLASS_JIT_LOG_ON_ERROR, lr.SWITCH_ON);
? ? ? ? return 0;
? ? }//end of action
? ? public int end() throws Throwable {
? ? ? ? return 0;
? ? }//end of end
}
所有以lr開頭的方法都是LR內(nèi)部方法格二,這里只用到了事務(wù)、日志等幾個竣蹦。
編譯之前我們的JAVA下載代碼顶猜,將包(com.test)放入LR腳本的目錄中,如圖:
打開LR腳本草添,運行試一下驶兜。
可以看到正常輸出了日志扼仲,再驗證一下下載到的文件是否完整远寸,找到輸出路徑,如“h:testtestvideo_20121106194419_-1”屠凶,用播放器打開這個文件驰后,正常播放。說明我們的腳本已經(jīng)OK了矗愧,下面就要做多用戶的測試了灶芝。
打開controller郑原,設(shè)置同時運行2個VUSER(為了保證負載機的網(wǎng)絡(luò)不成為瓶頸),運行場景夜涕。
可以看到犯犁,2個VUSER都按預(yù)期正常完成了。
從幾個方面驗證測試的有效性:
1是負載機的網(wǎng)絡(luò)利用女器,上面單用戶執(zhí)行腳本時下載速度是5M/s(apache服務(wù)器做了限速)酸役,這次兩個用戶同時下載達到了10M。
2是每個VUSER的輸出日志驾胆。
3是下載文件的完整性涣澡。