Python 使用twisted的基准测试工具

Python 使用twisted的基准测试工具,python,twisted,benchmarking,apachebench,Python,Twisted,Benchmarking,Apachebench,我正在尝试基于twisted编写一个web基准测试工具。Twisted是用于web应用程序的非常出色的异步框架。因为我刚刚开始使用这个框架两周,所以我面临一个问题,如下所示: 当我将这个基准测试工具与ApacheBench进行比较时,结果在相同的并发性上有很大的不同。以下是我的工具的结果: python pyab.py 50000 50 下面是Apache Bench的结果: ab-c 50-n 50000 在相同的url和并发性上,ApacheBench可以达到8000个请求/秒,而pyab只

我正在尝试基于twisted编写一个web基准测试工具。Twisted是用于web应用程序的非常出色的异步框架。因为我刚刚开始使用这个框架两周,所以我面临一个问题,如下所示: 当我将这个基准测试工具与ApacheBench进行比较时,结果在相同的并发性上有很大的不同。以下是我的工具的结果:

python pyab.py 50000 50

下面是Apache Bench的结果:

ab-c 50-n 50000

在相同的url和并发性上,ApacheBench可以达到8000个请求/秒,而pyab只能达到1000个请求/秒

这是我的codepyab.py:

from twisted.internet import reactor,threads
from twisted.internet.protocol import Protocol
from twisted.internet.defer import Deferred
from twisted.web.client import Agent
from twisted.web.client import HTTPConnectionPool
from twisted.web.http_headers import Headers
from twisted.python import log
import time, os, stat, logging, sys
from collections import Counter

logging.basicConfig(
    #filename= "/%s/log/%s.%s" % (RUN_DIR,RUN_MODULE,RUN_TIME),
    format="%(asctime)s [%(levelname)s] %(message)s",
    level=logging.WARNING,
    #level=logging.DEBUG,
    stream=sys.stdout
)

#log.startLogging(sys.stdout)
observer = log.PythonLoggingObserver()
observer.start()


class IgnoreBody(Protocol):
    def __init__(self, deferred, tl):
        self.deferred = deferred
        self.tl = tl

    def dataReceived(self, bytes):
        pass

    def connectionLost(self, reason):
        self.deferred.callback(None)


class Pyab:

    def __init__( self, n = 50000, concurrency = 100, url='http://203.90.245.26:8080/a.txt'):
        self.n = n
        self.url = url
        self.pool = HTTPConnectionPool(reactor, persistent=True)
        self.pool.maxPersistentPerHost = concurrency
        self.agent = Agent(reactor, connectTimeout = 5, pool = self.pool)
        #self.agent = Agent(reactor, connectTimeout = 5)

        self.time_start = time.time()
        self.max_worker = concurrency

        self.cnt = Counter({
            'worker' : 0 ,
            'req_made' : 0,
            'req_done' : 0,
            'req_error' : 0,
            })


    def monitor( self ):
        interval = int(time.time() - self.time_start)
        speed = 0
        if interval != 0:
            speed = int( self.cnt['req_done'] / interval )

        log.msg("speed:%d(q/s), worker:%d, interval:%d, req_made:%d, req_done:%d, req_error:%d" 
                % (speed, self.cnt['worker'], interval, self.cnt['req_made'], self.cnt['req_done'], self.cnt['req_error']), logLevel=logging.WARNING)


        reactor.callLater(1, lambda : self.monitor())

    def start( self ):
        self.keeprunning = True
        self.monitor()
        self.readMore()

    def stop( self ):
        self.keeprunning = False

    def readMore( self ):

        while self.cnt['worker'] < self.max_worker and self.cnt['req_done'] < self.n :
            self.make_request()

        if self.keeprunning and self.cnt['req_done'] < self.n:
            reactor.callLater( 0.0001, lambda: self.readMore() )
        else:
            reactor.stop()


    def make_request( self ):
        d = self.agent.request(
            'GET',
            #'http://examplexx.com/',
            #'http://example.com/',
            #'http://xa.xingcloud.com/v4/qvo/WDCXWD7500AADS-00M2B0_WD-WCAV5E38536685366?update0=ref0%2Ccor&update1=nation%2Ccn&action0=visit&_ts=1376397973636',
            #'http://203.90.245.26:8080/a.txt',
            self.url,
            Headers({'User-Agent': ['Twisted Web Client Example']}),
            None)

        self.cnt['worker'] += 1
        self.cnt['req_made'] += 1

        def cbResponse(resp):
            self.cnt['worker'] -= 1
            self.cnt['req_done'] += 1
            log.msg('response received')

            finished = Deferred()
            resp.deliverBody(IgnoreBody(finished, self))
            return finished

        def cbError(error):
            self.cnt['worker'] -= 1
            self.cnt['req_error'] += 1
            log.msg(error, logLevel=logging.ERROR)


        d.addCallback(cbResponse)
        d.addErrback(cbError)


if __name__ == '__main__' :
    if len(sys.argv) < 4:
        print "Usage: %s <n> <concurrency> <url>" % (sys.argv[0])
        sys.exit()

    ab = Pyab(n=int(sys.argv[1]), concurrency=int(sys.argv[2]), url=sys.argv[3])
    ab.start()
    reactor.run()
我的代码有问题吗?谢谢

当我上次使用它时,大家都知道ab有几十个严重的bug。有时这会导致它报告大量夸大的结果。有时它会报告负面结果。有时它会崩溃。我会尝试另一种工具,比如httperf,作为一种健康检查

但是,如果您的服务器确实那么快,那么您可能会遇到另一个问题

即使ab已经修复,您在这里讨论的是C程序与在CPython上运行的Python程序。在Python中,比C慢8倍实际上并不是那么糟糕,所以我不认为您的程序实际上有什么问题,只是它没有利用spawnProcess和多核并发


对于初学者,请查看您是否在PyPy上获得了更好的结果。

您的结果文本文件URL指向一个不太在Topict上的站点。这只是一个演示,web服务器是本地web服务器。哦,对不起,我想这些是你的结果的下载链接。如果你要使用示例域名,请始终使用example.com。谢谢你的建议,它们非常有用。首先,也许ApacheBench给了我一个错误的结果。我将使用httperf和其他基准测试工具来验证这一点。Sencod,服务器之所以如此之快,是因为它只提供一个静态文件,其中只包含一个hello。第三,当我同时运行两个pyab实例时,它们都可以达到1000req/sec。稍后我会逐一核对所有线索。再次感谢你!
Server Software:        nginx/1.4.1
Server Hostname:        203.90.245.26
Server Port:            8080

Document Path:          /a.txt
Document Length:        6 bytes

Concurrency Level:      50
Time taken for tests:   6.89937 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Total transferred:      12501750 bytes
HTML transferred:       300042 bytes
Requests per second:    8210.27 [#/sec] (mean)
Time per request:       6.090 [ms] (mean)
Time per request:       0.122 [ms] (mean, across all concurrent requests)
Transfer rate:          2004.62 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.8      0       4
Processing:     1    5   3.4      5     110
Waiting:        0    2   3.6      2     109
Total:          1    5   3.5      5     110

Percentage of the requests served within a certain time (ms)
  50%      5
  66%      6
  75%      6
  80%      6
  90%      7
  95%      7
  98%      8
  99%      8
 100%    110 (longest request)
from twisted.internet import reactor,threads
from twisted.internet.protocol import Protocol
from twisted.internet.defer import Deferred
from twisted.web.client import Agent
from twisted.web.client import HTTPConnectionPool
from twisted.web.http_headers import Headers
from twisted.python import log
import time, os, stat, logging, sys
from collections import Counter

logging.basicConfig(
    #filename= "/%s/log/%s.%s" % (RUN_DIR,RUN_MODULE,RUN_TIME),
    format="%(asctime)s [%(levelname)s] %(message)s",
    level=logging.WARNING,
    #level=logging.DEBUG,
    stream=sys.stdout
)

#log.startLogging(sys.stdout)
observer = log.PythonLoggingObserver()
observer.start()


class IgnoreBody(Protocol):
    def __init__(self, deferred, tl):
        self.deferred = deferred
        self.tl = tl

    def dataReceived(self, bytes):
        pass

    def connectionLost(self, reason):
        self.deferred.callback(None)


class Pyab:

    def __init__( self, n = 50000, concurrency = 100, url='http://203.90.245.26:8080/a.txt'):
        self.n = n
        self.url = url
        self.pool = HTTPConnectionPool(reactor, persistent=True)
        self.pool.maxPersistentPerHost = concurrency
        self.agent = Agent(reactor, connectTimeout = 5, pool = self.pool)
        #self.agent = Agent(reactor, connectTimeout = 5)

        self.time_start = time.time()
        self.max_worker = concurrency

        self.cnt = Counter({
            'worker' : 0 ,
            'req_made' : 0,
            'req_done' : 0,
            'req_error' : 0,
            })


    def monitor( self ):
        interval = int(time.time() - self.time_start)
        speed = 0
        if interval != 0:
            speed = int( self.cnt['req_done'] / interval )

        log.msg("speed:%d(q/s), worker:%d, interval:%d, req_made:%d, req_done:%d, req_error:%d" 
                % (speed, self.cnt['worker'], interval, self.cnt['req_made'], self.cnt['req_done'], self.cnt['req_error']), logLevel=logging.WARNING)


        reactor.callLater(1, lambda : self.monitor())

    def start( self ):
        self.keeprunning = True
        self.monitor()
        self.readMore()

    def stop( self ):
        self.keeprunning = False

    def readMore( self ):

        while self.cnt['worker'] < self.max_worker and self.cnt['req_done'] < self.n :
            self.make_request()

        if self.keeprunning and self.cnt['req_done'] < self.n:
            reactor.callLater( 0.0001, lambda: self.readMore() )
        else:
            reactor.stop()


    def make_request( self ):
        d = self.agent.request(
            'GET',
            #'http://examplexx.com/',
            #'http://example.com/',
            #'http://xa.xingcloud.com/v4/qvo/WDCXWD7500AADS-00M2B0_WD-WCAV5E38536685366?update0=ref0%2Ccor&update1=nation%2Ccn&action0=visit&_ts=1376397973636',
            #'http://203.90.245.26:8080/a.txt',
            self.url,
            Headers({'User-Agent': ['Twisted Web Client Example']}),
            None)

        self.cnt['worker'] += 1
        self.cnt['req_made'] += 1

        def cbResponse(resp):
            self.cnt['worker'] -= 1
            self.cnt['req_done'] += 1
            log.msg('response received')

            finished = Deferred()
            resp.deliverBody(IgnoreBody(finished, self))
            return finished

        def cbError(error):
            self.cnt['worker'] -= 1
            self.cnt['req_error'] += 1
            log.msg(error, logLevel=logging.ERROR)


        d.addCallback(cbResponse)
        d.addErrback(cbError)


if __name__ == '__main__' :
    if len(sys.argv) < 4:
        print "Usage: %s <n> <concurrency> <url>" % (sys.argv[0])
        sys.exit()

    ab = Pyab(n=int(sys.argv[1]), concurrency=int(sys.argv[2]), url=sys.argv[3])
    ab.start()
    reactor.run()