Apache Bench(AB)的下載、安裝與訪問、測試


Apache Bench(AB)的下載

  首先我們得從Apache的官網上下載Apache Bench(AB)官網上的版本有32位與64位兩個不同版本,這里我們可以根據我們的操作系統版本選擇對應的軟件安裝包,由於我的操作系統位64位,所以說在這里我直接下載64位版本的Apache Bench(AB)。

 

 

 由於該網站為外網網站,所以說我們在用瀏覽器下載時不免會有些緩慢,如果遇到這種情況,請直接復制下載鏈接,然后使用迅雷進行下載,比如說我使用迅雷下載該軟件,下載一個將近17M的安裝包,所需的下載時間為3秒,之所以會這么快,是因為它有鏡像加速功能。

 

 

 

Apache Bench(AB)的安裝

解壓到安裝目錄

  解壓我們下載好的httpd-2.4.34-win64-VC15.zip到指定的安裝目錄,比如說我這里的安裝目錄為:

 

F:\Dev\httpd-2.4.34\Apache24

修改配置文件

  修改Apache Bench(AB)的配置文件,該配置文件在軟件安裝目錄下的conf/httpd.conf,用編輯器打開該配置文件,將其中的服務器根目錄修改成本地的安裝目錄,在這里,我的本地安裝目錄為:

Define SRVROOT "F:/Dev/httpd-2.4.34/Apache24"

修改Apache Bench(AB)的監聽端口號,將其端口號由默認的80端口改成8088,如下:

Listen 8088

之所以這樣改,主要是為了防止端口號沖突。比如說IIS一般喜歡占用80端口,而Tomcat一般喜歡占用8080端口,而上述兩個端口號往往是web開發過程中經常會使用到的端口號,所以為了防止Apache Bench(AB)的端口號可能與上述兩種端口號沖突,這里我將其端口號改成了8088。

修改servername

ServerName www.studylaravel.com:80

注冊成系統服務

  以管理員身份運行cmd控制台,將控制台的光標切換成軟件的安裝目錄,在該安裝目錄中,切換到bin目錄,在其中運行如下指令:

httpd -k install

當我們看到下圖中的successfully installed信息時,此時就說明我們的軟件已經成功的注冊成系統服務了。

 

注冊后的系統服務如下所示:

 

 

啟動並訪問Apache Bench(AB)

啟動Apache Bench(AB)

  我們可以采用圖形界面的方式進行訪問,在Apache Bench(AB)的安裝目錄下,找到bin/ApacheMonitor.exe,雙擊並啟動模擬器,此時我們可以看到桌面的右下角中出現了該軟件的小圖標,我們可以通過該小圖標以圖形界面的形式啟動該Apache Bench(AB)。

  我們通過點擊小圖標,然后依次點擊Apache 2.4 -> Start來啟動該軟件。

訪問Apache Bench(AB)

  在瀏覽器地址欄中輸入http://localhost:8088/進行訪問,當我們看到下面的信息時,此時就說明我們的軟件已經能夠正常使用了。

 

 參數很多,一般我們用 -c 和 -n 參數就可以了。例如:

ab -c 5000 -n 600 http://127.0.0.1/index.php

ApacheBench用法詳解:

在Linux系統,一般安裝好Apache后可以直接執行;
# ab -n 4000 -c 1000 http://www.ha97.com/

如果是Win系統下,打開cmd命令行窗口,cd到apache安裝目錄的bin目錄下;

-n后面的4000代表總共發出4000個請求;-c后面的1000表示采用1000個並發(模擬1000個人同時訪問),后面的網址表示測試的目標URL。

 

 結果分析:

This is ApacheBench, Version 2.3
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.80.157 (be patient)
Completed 400 requests
Completed 800 requests
Completed 1200 requests
Completed 1600 requests
Completed 2000 requests
Completed 2400 requests
Completed 2800 requests
Completed 3200 requests
Completed 3600 requests
Completed 4000 requests
Finished 4000 requests

Server Software: Apache/2.2.15
Server Hostname: 192.168.80.157
Server Port: 80

Document Path: /phpinfo.php
#測試的頁面
Document Length: 50797 bytes
#頁面大小

Concurrency Level: 1000
#測試的並發數
Time taken for tests: 11.846 seconds
#整個測試持續的時間
Complete requests: 4000
#完成的請求數量
Failed requests: 0
#失敗的請求數量
Write errors: 0
Total transferred: 204586997 bytes
#整個過程中的網絡傳輸量
HTML transferred: 203479961 bytes
#整個過程中的HTML內容傳輸量
Requests per second: 337.67 [#/sec] (mean)
#最重要的指標之一,相當於LR中的每秒事務數,后面括號中的mean表示這是一個平均值
Time per request: 2961.449 [ms] (mean)
#最重要的指標之二,相當於LR中的平均事務響應時間,后面括號中的mean表示這是一個平均值
Time per request: 2.961 [ms] (mean, across all concurrent requests)
#每個連接請求實際運行時間的平均值
Transfer rate: 16866.07 [Kbytes/sec] received
#平均每秒網絡上的流量,可以幫助排除是否存在網絡流量過大導致響應時間延長的問題
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 483 1773.5 11 9052
Processing: 2 556 1459.1 255 11763
Waiting: 1 515 1459.8 220 11756
Total: 139 1039 2296.6 275 11843
#網絡上消耗的時間的分解,各項數據的具體算法還不是很清楚

Percentage of the requests served within a certain time (ms)
50% 275
66% 298
75% 328
80% 373
90% 3260
95% 9075
98% 9267
99% 11713
100% 11843 (longest request)
#整個場景中所有請求的響應情況。在場景中每個請求都有一個響應時間,其中50%的用戶響應時間小於275毫秒,66%的用戶響應時間小於298毫秒,最大的響應時間小於11843毫秒。對於並發請求,cpu實際上並不是同時處理的,而是按照每個請求獲得的時間片逐個輪轉處理的,所以基本上第一個Time per request時間約等於第二個Time per request時間乘以並發請求數。

 並發測試

准備兩張數據表:

CREATE TABLE `concurrent` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `version` bigint(20) unsigned NOT NULL,
  `stock` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `concurrent_log` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2967 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

數據:

INSERT INTO `ceshi1123`.`concurrent` (`id`, `name`, `version`, `stock`) VALUES ('1', '5f69af49eb1d6', '0', '50');

目前庫存為50個,下面進行代碼編寫:

    public function update_default_stock()
    {
        $name = uniqid();
        try{
//            DB::beginTransaction();
            $c = DB::table('concurrent')->where('id', 1)->first();
            if($c->stock < 1){
                throw new \Exception("error:stock");
            }
            $a = DB::table('concurrent')->update(['name'=>$name, 'version'=>DB::raw('version+1'), 'stock'=>DB::raw('stock-1')]);
            DB::table('concurrent_log')->insert(['content'=>$name.":stock:".$c->stock.":default_stock:".$a]);
//            DB::commit();
        }catch (\Throwable $e){
//            DB::rollBack();
            DB::table('concurrent_log')->insert(['content'=>$e->getMessage()]);
        }
    }

執行測試命令,-n表示總共100次請求,-c表示每次100個並發(同時100個訪問):

ab -n 100 -c 100 http://www.studylaravel.com/concurrent/stock

 

 

 執行完畢,但是發現庫存已經超出范圍,執行了52次更新:

 

 

 第二次測試,加上事務

代碼修改:

public function update_default_stock()
    {
        $name = uniqid();
        try{
            DB::beginTransaction();
            $c = DB::table('concurrent')->where('id', 1)->first();
            if($c->stock < 1){
                throw new \Exception("error:stock");
            }
            $a = DB::table('concurrent')->update(['name'=>$name, 'version'=>DB::raw('version+1'), 'stock'=>DB::raw('stock-1')]);
            DB::table('concurrent_log')->insert(['content'=>$name.":stock:".$c->stock.":default_stock:".$a]);
            DB::commit();
        }catch (\Throwable $e){
            DB::rollBack();
            DB::table('concurrent_log')->insert(['content'=>$e->getMessage()]);
        }
    }

還原測試前的數據:

 

 

 開始測試:

 

 

 結果為:

 

 

 還是沒有解決賣超的問題。怎么辦呢?

使用共享鎖試試

修改代碼:

public function update_sharedLock_stock()
    {
        $name = uniqid();
        try{
            DB::beginTransaction();
            $c = DB::table('concurrent')->where('id', 1)->sharedLock()->first();
            if($c->stock < 1){
                throw new \Exception("error:stock");
            }
            $a = DB::table('concurrent')->update(['name'=>$name, 'version'=>DB::raw('version+1'), 'stock'=>DB::raw('stock-1')]);
            DB::table('concurrent_log')->insert(['content'=>$name.":stock:".$c->stock.":default_stock:".$a]);
            DB::commit();
        }catch (\Throwable $e){
            DB::rollBack();
            DB::table('concurrent_log')->insert(['content'=>$e->getMessage()]);
        }
    }

共享鎖說明:https://www.cnblogs.com/jiangxiaobo/p/13705711.html,共享鎖對得到鎖的用戶是運行修改、讀取,但是未得到鎖的用戶只能讀取,不能修改。

繼續測試:

 

 

 成功部分,錯誤部分是因為死鎖造成失敗的:

 

 

 

SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction (SQL: update `concurrent` set `name` = 5f69b2d9c1d2b, `version` = version+1, `stock` = stock-1)

使用排它鎖試試

修改代碼:

public function update_lockForUpdate_stock()
    {
        $name = uniqid();
        try{
            DB::beginTransaction();
            $c = DB::table('concurrent')->where('id', 1)->lockForUpdate()->first();
            if($c->stock < 1){
                throw new \Exception("error:stock");
            }
            $a = DB::table('concurrent')->update(['name'=>$name, 'version'=>DB::raw('version+1'), 'stock'=>DB::raw('stock-1')]);
            DB::table('concurrent_log')->insert(['content'=>$name.":stock:".$c->stock.":default_stock:".$a]);
            DB::commit();
        }catch (\Throwable $e){
            DB::rollBack();
            DB::table('concurrent_log')->insert(['content'=>$e->getMessage()]);
        }
    }

繼續測試:

 

 

 發現排它鎖是可以的,就是會阻塞,造成請求過慢,但是保證數據穩定

 

使用樂觀鎖試試

修改代碼:

public function update_optimistic_lock_stock()
    {
        $name = uniqid();
        try{
            DB::beginTransaction();
            $c = DB::table('concurrent')->where('id', 1)->first();
            if($c->stock < 1){
                throw new \Exception("error:stock");
            }
            $a = DB::table('concurrent')
                ->where('id', 1)
                ->where('version', $c->version)
                ->update(['name'=>$name, 'version'=>DB::raw('version+1'), 'stock'=>DB::raw('stock-1')]);
            if(empty($a)){
                throw new \Exception("error:nothing");
            }
            DB::table('concurrent_log')->insert(['content'=>$name.":stock:".$c->stock]);
            DB::commit();
        }catch (\Throwable $e){
            DB::rollBack();
            DB::table('concurrent_log')->insert(['content'=>$e->getMessage()]);
        }
        echo 1;
    }

繼續測試:

 

 

 發現只有部分成功了!,看下日志:

 

 

 發現大部分請求什么都失敗了,因為同時更新相同version只能有一個成功。這里為了避免這種情況,可以采用下面這種方式來處理:

public function update_optimistic_lock_stock2()
    {
        $name = uniqid();
        try{
            DB::beginTransaction();
            $c = DB::table('concurrent')->where('id', 1)->first();
            if($c->stock < 1){
                throw new \Exception("error:stock");
            }
            $a = DB::table('concurrent')
                ->where('id', 1)
                ->where('stock', '>', 0)
                ->update(['name'=>$name, 'version'=>DB::raw('version+1'), 'stock'=>DB::raw('stock-1')]);
            if(empty($a)){
                throw new \Exception("error:nothing");
            }
            DB::table('concurrent_log')->insert(['content'=>$name.":stock:".$c->stock]);
            DB::commit();
        }catch (\Throwable $e){
            DB::rollBack();
            DB::table('concurrent_log')->insert(['content'=>$e->getMessage()]);
        }
        echo 1;
    }

繼續測試:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM