[雪峰磁針石部落格]multi-mechanize效能測試工具

書籍尋找發表於2018-08-16

Multi-Mechanize簡介

Multi-Mechanize 是一個開源的效能和負載測試框架,它併發執行多個 Python
指令碼對網站或者服務生成負載(組合事務)。測試輸出報告儲存為HTML或JMeter的相容的XML。Multi-Mechanize最常用於web效能

和可擴充套件性(scalability)測試,也適用於任何python可以訪問的API。尤其適合後臺效能測試。稍微懂點程式設計的話,這個工具會遠強過商業
的效能測試工具。

主要特性:

  • 支援各種 HTTP methods

  • 高階超連結和HTML表單支援

  • 支援 SSL

  • 自動處理 Cookies

  • 可設定HTTP頭

  • 自動處理重定向

  • 支援代理

  • 支援 HTTP 認證

600
圖片.png

安裝

使 用標準的python安裝方式。注意,需要安裝matplotlib以支援作圖,在centos6下面可以這樣安裝yum -y
install python27-matplotlib。multi-mechanize採用標準的python安裝方式pip install
multi-mechanize或者easy_install multi-mechanize這裡都以linux(centos)為例。

快速入門

建立專案

# multimech-newproject my_project

執行專案


  # multimech-run my_project
    user_groups:  2
    threads: 6
  [================100%==================]  30s/30s   transactions: 119  timers: 119  errors: 0
  waiting for all requests to finish...
  analyzing results...
  transactions: 125
  errors: 0
  test start: 2013-09-13 11:47:47
  test finish: 2013-09-13 11:48:16
  created: ./my_project/results/results_2013.09.13_11.47.46/results.html
  done.

如果有出現figure沒有定義,請在相關檔案的頭部從matplotlib匯入。

測試結果參見:

700
image.png
700
圖片.png
700
圖片.png

目錄結構

每個測試專案包含以下內容:

  • config.cfg的配置檔案。用於設定測試選項。

  • test_scripts/虛擬使用者指令碼的目錄。在這裡新增您的測試指令碼。

  • results/:結果儲存目錄。對於每個測試都聲稱一個時間戳目錄,裡面包含結果的報告。

multimech-newproject,預設生成一個隨機數的指令碼。指令碼v_user.py如下:

  import randomimport timeclass Transaction(object):
      def __init__(self):
          pass
      def run(self):
          r = random.uniform(1, 2)
          time.sleep(r)
          self.custom_timers[`Example_Timer`] = rif __name__ == `__main__`:
      trans = Transaction()
      trans.run()
      print trans.custom_timers

配置引數的含義如下:

  • run_time: duration of test (seconds) 測試的執行時間

  • rampup: duration of user rampup (seconds) 多少秒內發完請求

  • results_ts_interval: time series interval for results analysis (seconds) 結果分析時間

  • progress_bar: turn on/off console progress bar during test run 是否顯示進度條

  • console_logging: turn on/off logging to stdout 是否輸出到stdout

  • xml_report: turn on/off xml/jtl report 是否生成xml報告。

  • results_database: database connection string (optional) 儲存結果的資料庫連線字串(可選)

  • post_run_script: hook to call a script at test completion (optional) 呼叫的善後指令碼(可選)

更多介紹參見: http://testutils.org/multi-mechanize/configfile.html

指令碼書寫

用Python書寫,測試指令碼模擬虛擬使用者對網站/服務/ API的請求,指令碼定義了使用者事務,更多內容參見指令碼手冊:http://testutils.org/multi-mechanize/scripts.html#scripts-label

每個指令碼必須實現一個Transaction()類。這個類必須實現一個run()方法。基本的測試指令碼結構如下:

  class Transaction(object):
      def run(self):
          # do something here
          return

執行期間,Transaction()例項化一次,run()方法則反覆呼叫:

  class Transaction(object):
      def __init__(self):
          # do per-user user setup here
          # this gets called once on user creation
          return
      def run(self):
          # do user actions here
          # this gets called repeatedly
          return

從結構上看,如果每次run如果需要setup和teardown,時間也會計算在run裡面,會顯得事務處理的時間更長。這個就需要使用定時器來精確計時。

另外指令碼建議先除錯之後在執行,因為Multi-Mechanize有可能報錯不夠精準。可以這樣執行:# python
v_suds.py。v_suds.py是你實際使用的指令碼名。另外suds這個庫好像實現時效能一般,併發200時,客戶端cpu佔用率經常會100%,為此web

service如果要上大量使用者的話,建議用其他庫替代,比如soapPy。進一步提升效率可以試用python的ctypes模組,或者cython(效能接近c語言)。不過Multi-Mechanize的多程式是基於python的,實在對效能有相當高的要求,就只能全部用c書寫了。

下例使用mechanize進行web測試。

 class Transaction(object):
      def __init__(self):
          pass
      def run(self):
          br = mechanize.Browser()
          br.set_handle_robots(False)
          resp = br.open(`http://192.168.4.13/env.htm`)
          assert (resp.code == 200), `Bad Response: HTTP %s` % resp.codes        
          assert (`service name` in resp.get_data())

下面用httplib庫重寫指令碼,並增加定時器。通過定時器,可以分析各個步驟的耗時。

import httplib
import time

class Transaction(object):
    def run(self):
        conn = httplib.HTTPConnection(`192.168.4.13`)
        start = time.time()
        conn.request(`GET`, `/env.htm`)
        request_time = time.time()
        resp = conn.getresponse()
        response_time = time.time()
        conn.close()
        transfer_time = time.time()
        self.custom_timers[`request sent`] = request_time - start                          
        self.custom_timers[`response received`] = response_time - start        
        self.custom_timers[`content transferred`] = transfer_time - start        
        assert (resp.status == 200), `Bad Response: HTTP %s` % resp.status
 
 if __name__ == `__main__`:
    trans = Transaction()
    trans.run()
    for timer in (`request sent`, `response received`, `content transferred`):
        print `%s: %.5f secs` % (timer, trans.custom_timers[timer])

http://testutils.org/multi-mechanize/scripts.html#scripts-label 還有更多的例項。


import mechanize
import time


class Transaction(object):

    def __init__(self):
        pass

    def run(self):
        # create a Browser instance
        br = mechanize.Browser()
        # don`t bother with robots.txt
        br.set_handle_robots(False)
        # add a custom header so wikipedia allows our requests
        br.addheaders = [(`User-agent`, `Mozilla/5.0 Compatible`)]

        # start the timer
        start_timer = time.time()
        # submit the request
        resp = br.open(`http://www.wikipedia.org/`)
        resp.read()
        # stop the timer
        latency = time.time() - start_timer

        # store the custom timer
        self.custom_timers[`Load_Front_Page`] = latency

        # verify responses are valid
        assert (resp.code == 200), `Bad Response: HTTP %s` % resp.code
        assert (`Wikipedia, the free encyclopedia` in resp.get_data())

        # think-time
        time.sleep(2)

        # select first (zero-based) form on page
        br.select_form(nr=0)
        # set form field
        br.form[`search`] = `foo`

        # start the timer
        start_timer = time.time()
        # submit the form
        resp = br.submit()
        resp.read()
        # stop the timer
        latency = time.time() - start_timer

        # store the custom timer
        self.custom_timers[`Search`] = latency

        # verify responses are valid
        assert (resp.code == 200), `Bad Response: HTTP %s` % resp.code
        assert (`foobar` in resp.get_data()), `Text Assertion Failed`

        # think-time
        time.sleep(2)
import urllib2
import time

class Transaction(object):
    def run(self):
        start_timer = time.time()
        resp = urllib2.urlopen(`http://www.example.com/`)
        content = resp.read()
        latency = time.time() - start_timer

        self.custom_timers[`Example_Homepage`] = latency

        assert (resp.code == 200), `Bad Response: HTTP %s` % resp.code
        assert (`Example Web Page` in content), `Text Assertion Failed`

import urllib2
import time

class Transaction(object):
    def __init__(self):
        self.custom_timers = {}
        with open(`soap.xml`) as f:
            self.soap_body = f.read()

    def run(self):
        req = urllib2.Request(url=`http://www.foo.com/service`, data=self.soap_body)
        req.add_header(`Content-Type`, `application/soap+xml`)
        req.add_header(`SOAPAction`, `http://www.foo.com/action`)

        start_timer = time.time()
        resp = urllib2.urlopen(req)
        content = resp.read()
        latency = time.time() - start_timer

        self.custom_timers[`Example_SOAP_Msg`] = latency

        assert (resp.code == 200), `Bad Response: HTTP %s` % resp.code
        assert (`Example SOAP Response` in content), `Text Assertion Failed`

參考資料


相關文章