1

我正在尝试编写一个基于 twisted 的 Web 基准测试工具。Twisted 是一个非常棒的 Web 应用异步框架。因为我刚开始使用这个框架只有两个星期,所以我遇到了一个问题,就是这样:当我测试这个基准测试工具时,与 ApacheBench 进行比较,结果在相同的并发上差异很大。这是我的工具的结果:

python pyab.py 50000 50 http://xx.com/a.txt

speed:1063(q/s), worker:50, interval:7, req_made:7493, req_done:7443, req_error:0

这是 Apache Bench 的结果:

ab -c 50 -n 50000 http://xx.com/a.txt

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)

在相同的 url 和并发下,ApacheBench 可以达到 8000 req/sec,而 pyab 只能达到 1000 req/sec。

这是我的代码(pyab.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()

我的代码有什么问题吗?谢谢!

4

1 回答 1

0

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

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

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

对于初学者,看看你是否在 PyPy 上得到了更好的结果。

于 2013-08-15T22:18:05.580 回答