欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析

程序员文章站 2022-05-11 22:50:57
...

Blog地址:https://www.jiangdog.com/blog/tornado-motor-ab

背景

在利用Python开发web项目时,可以使用pymongomongoengine等库连接mongodb。当使用Tornado框架时,为了利用其异步的特性,还可以使用motor来作为数据库驱动。无论是mongoengine还是motor实际上应该都是对pymongo的封装。

  • mongoengine提供了ORM映射的功能,将Document封装成类,通过实例的方法来操作Document
  • motor能够和Tornado或协程配合使用,异步调用提升性能。

参考网上的文章并自己写了一些简单的代码来测试在使用Tornado框架下,这三种数据库驱动的性能。

测试工具

使用ab来做这次简单测试。

yum provides /usr/bin/ab
yum install httpd-tools

ab基本参数介绍:

  • -n 指定总共发起的请求数量。
  • -c 同时发起的请求数量,并发请求数。
  • -p 指定post方式提交的数据的文件,文件内容格式为a=1&b=2
  • -T 指定content-type

测试服务器配置及环境

服务器是利用了阿里云的基本的云服务器ECS实例:

  • 1核 1GB 带宽1Mbps
  • CentOS 7.2

mongodb和测试web服务都是安装或部署在该实例上:

  • mongodb 3.4
  • python 3.5.1
  • tornado 4.4.2
  • pymongo 3.6.0
  • mongoengine 0.15.0
  • motor 1.1

编写简单的服务端代码并利用ab进行测试

pymongo:

#!/usr/bin/python
# -*- coding:utf-8 -*-
import tornado.web
import tornado.gen
from pymongo import MongoClient
from tornado.httpserver import HTTPServer


class MessageListHandler(tornado.web.RequestHandler):

    def get(self):
        message_list = self.settings['db'].messages.find().sort([('_id', -1)]).limit(50)
        self.render('msg_list.html', message_list=message_list)


class MessageAddHandler(tornado.web.RequestHandler):

    def get(self):
        self.render('msg_add.html')

    def post(self):
        msg = self.get_argument('msg')

        res = self.settings['db'].messages.insert_one({'msg': msg})

        self.redirect('/message/list/')


class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r'/message/list/', MessageListHandler),
            (r'/message/add/', MessageAddHandler),
        ]

        db = MongoClient('mongodb://127.0.0.1:27017', maxPoolSize=200).test_motor

        settings = dict(
            db=db,
            debug=False,
        )

        tornado.web.Application.__init__(self, handlers, **settings)


def main():
    app = Application()
    httpserver = HTTPServer(app, xheaders=True)
    httpserver.listen(80)

    tornado.ioloop.IOLoop.instance().start()


if __name__ == '__main__':
    main()
  • get请求查询信息列表。

利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析

  • post请求添加数据:
    利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析

mongoengine:

#!/usr/bin/python
# -*- coding:utf-8 -*-
import tornado.web
import tornado.gen
from mongoengine import Document, StringField, connect
from tornado.httpserver import HTTPServer


class Messages(Document):
    msg = StringField(required=True)


class MessageListHandler(tornado.web.RequestHandler):

    def get(self):
        message_list = Messages.objects.filter().order_by('-id').limit(50)
        self.render('msg_list.html', message_list=message_list)


class MessageAddHandler(tornado.web.RequestHandler):

    def get(self):
        self.render('msg_add.html')

    def post(self):
        msg = self.get_argument('msg')

        res = Messages(msg=msg).save()

        self.redirect('/message/list/')


class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r'/message/list/', MessageListHandler),
            (r'/message/add/', MessageAddHandler),
        ]

        connect('test_motor', host='127.0.0.1', port=27017, maxPoolSize=200)

        settings = dict(
            debug=False,
        )

        tornado.web.Application.__init__(self, handlers, **settings)


def main():
    app = Application()
    httpserver = HTTPServer(app, xheaders=True)
    httpserver.listen(80)

    tornado.ioloop.IOLoop.instance().start()


if __name__ == '__main__':
    main()
  • get请求数据列表:
    利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析
  • post添加数据:
    利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析

    motor:

#!/usr/bin/python
# -*- coding:utf-8 -*-
import motor
import tornado.web
import tornado.gen
from tornado.httpserver import HTTPServer


class MessageListHandler(tornado.web.RequestHandler):

    @tornado.gen.coroutine
    def get(self):
        message_list = yield self.settings['db'].messages.find().sort([('_id', -1)]).limit(50).to_list(length=None)
        self.render('msg_list.html', message_list=message_list)


class MessageAddHandler(tornado.web.RequestHandler):

    def get(self):
        self.render('msg_add.html')

    @tornado.gen.coroutine
    def post(self):
        msg = self.get_argument('msg')

        res = yield self.settings['db'].messages.insert_one({'msg': msg})

        self.redirect('/message/list/')


class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r'/message/list/', MessageListHandler),
            (r'/message/add/', MessageAddHandler),
        ]

        db = motor.motor_tornado.MotorClient('mongodb://127.0.0.1:27017', maxPoolSize=200).test_motor

        settings = dict(
            db=db,
            debug=False,
        )

        tornado.web.Application.__init__(self, handlers, **settings)


def main():
    app = Application()
    httpserver = HTTPServer(app, xheaders=True)
    httpserver.listen(80)

    tornado.ioloop.IOLoop.instance().start()


if __name__ == '__main__':
    main()
  • get请求数据列表:
    利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析
  • post新增数据:
    利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析

分析相关的测试数据

由上述简单测试不难发现,在n1000c200(总请求1000,并发200)的情况下各项测试数据相差并不是很大。
接着更改指标测试了n5000c500n8000c8000,发现依旧差异不大,自认为再测下去也没什么意义。
发现每次测试接收数据速率一直都是70kb/s左右,发现是否和带宽限制有关,改到服务器本地和内网进行简单测试。

  1. n1000c200
    • pymongo
      利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析
    • mongoengine
      利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析
    • motor
      利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析

由上述不难发现,当传输速度足够时mongoengine相比起pymongomotor的处理性能下降了很多很多。
继续更改测试指标试图发现pymongomotor的差异。

  1. n5000c500
    • pymongo
      利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析
    • motor
      利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析

值得一提的是在ab进行n5000c500测试的时候,使用pymongo时经常会出现apr_socket_recv: Connection reset by peer (104)错误,且更大数量级请求和并发时一直出现导致所有请求无法完成,查了资料也没能解除这个限制;但在使用motor时,基本没有出现这个错误信息,且对于n8000c800也能正常测试,如下图。
利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析

考虑到很多情况下不会有这么多并发和请求,重新测试了现对较少请求和并发(n1000c50等)的情况。

  1. pymongo
    利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析
  2. mongoengine
    利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析
  3. motor
    利用ab测试工具对Tornado下使用mongodb驱动的性能简单测试分析

依旧是pymongomotor表现的出色,mongoengine感觉差了一个档次。

End

  1. 不管是在请求总量较少并发量小(如总请求1000,并发50、甚至总请求200,并发20)的情况下,pymongomotor始终比mongoengine表现的出色。使用pymongomotor服务器处理每次请求的速度甚至比mongoengine快一倍,QPS和用户平均等待时间也相差很多。
  2. 但在使用mongoengine编写代码时,提供了现成的类似ORM的作用,可能编写代码相对较为方便;而pymongo原生的始终是通过字典来操作的;motor也是通过字典切需要结合tornado的异步特性来使用。
  3. pymongomotor在上述较少数据量简单测试代码是表现的都很出色,相差不大(或者在相对少请求量和并发时,pymongomotor略微好一点点),但在请求量很大并发大时,由于一些未能解决的错误,没有进行很好的完整的测试。之后可能需要添加更加复杂的逻辑代码,可能需要增大数据量,增加每次请求中访问数据库的次数,理论上应该motor会表现的更好。
  4. 简单增加了一些无用的逻辑,单纯增加访问数据库的次数,再对pymongomotor进行一些测试。
class MessageListHandler(tornado.web.RequestHandler):

    def get(self):
        # message_list = self.settings['db'].messages.find().sort([('_id', -1)]).limit(50)

        # message_list = self.settings['db'].messages.find().sort([('_id', -1)]).limit(500)

        message_list = []

        for i in range(10):
            message_list.extend(self.settings['db'].messages.find().sort([('_id', -1)]).limit(50))

        self.render('msg_list.html', message_list=message_list)
class MessageListHandler(tornado.web.RequestHandler):

    @tornado.gen.coroutine
    def get(self):
        # message_list = yield self.settings['db'].messages.find().sort([('_id', -1)]).limit(50).to_list(length=None)

        # message_list = yield self.settings['db'].messages.find().sort([('_id', -1)]).limit(500).to_list(length=None)

        message_list = []

        for i in range(10):
            temp_list = yield self.settings['db'].messages.find().sort([('_id', -1)]).limit(50).to_list(length=None)
            message_list.extend(temp_list)

        self.render('msg_list.html', message_list=message_list)

motor反而处理性能变的更差了。

参考链接:

  1. What are the Tornado and Mongodb blocking and asynchronous considerations?
  2. https://emptysqua.re/blog/introducing-motor-an-asynchronous-mongodb-driver-for-python-and-tornado/
  3. Apache ab测试工具使用方法(无参、get传参、post传参)
  4. tornado的mongo驱动选择,pymongo,motor,asyncmongo还是其他?
  5. Tornado 中 PyMongo Motor MongoEngine 的性能测试,这两个测试结果,一个pymongo好,一个motor好。

后续

重新在公司内部测试服务器(具体配置未知,但配置肯定比之前的阿里云高)用python 2.7进行了测试,测试代码也进行了稍作优化,增加了一定的业务逻辑,同时利用mongostat简单监测了mongodb的各项数据,结果发现motor框架在高并发,操作数据库代码较多时,表现的性能更好。
相关代码