rabbitMQ学习六:队列性能测试及优化方法

性能测试

针对每个需要生产/消费者与rabbitmq进行通讯的方法进行测试

测试环境:

  • 排除网络IO的干扰,采用生产者和消费者都在本地服务器的方式
  • 内存16G,CPU4核,3.1GHZ
  • 操作系统:oracle-linux
  • python版本:3.6.3

测试内容

  • 创建10万个connection连接的平均速度

  • 创建10万个信道的平均速度

  • 创建10万个相同队列的平均速度

  • 创建10万个相同直连交换机的平均速度

  • 创建10万个相同主题交换机的平均速度

  • 创建10万个交换机和队列绑定的平均速度

  • 投递10万条10字节消息的的平均速度

  • 投递10万条300K消息的的平均速度

测试使用的脚本

#!/usr/bin/env python
import threading
import time
import pika
"""这是队列生产者的客户端配置文件"""
HOST = '192.168.1.22'
PORT = ''
MQ_NAME = 'eeg'
FILENAME = './eeg2.txt'
EXCHANGE = 'eegs'
ROUT_KEY = 'eeg'

USERNAME = 'passwd'
PASSWD = 'passwd'


class Client(object):
    def __init__(self, host, message,
                 rout_key=ROUT_KEY, filename=FILENAME,
                 queue_name=MQ_NAME, exchange=EXCHANGE,
                 username=USERNAME, passwd=PASSWD):
        self.__message = message
        self.__host = host
        self.__rout = rout_key
        self.__filename = filename
        self.__username = username
        self.__queue_name = queue_name
        self.__exchange = exchange
        self.__passwd = passwd

    # 设置参数
    def add_pars(self):
        # 添加用户名和密码
        credentials = pika.PlainCredentials(self.__username, self.__passwd)
        # 配置连接参数
        parameters = pika.ConnectionParameters(
            host=self.__host, credentials=credentials)
        return parameters

    # 连接mq队列
    def connect_mq(self, parameters):
        try:
            # 创建一个连接对象
            connection = pika.BlockingConnection(parameters)
        except Exception as e:
            print(e)
        else:
            return connection

    # 创建信道
    def channel_mq(self, connection, prefetch=1):
        try:
            channel = connection.channel()
            # 公平调度
            channel.basic_qos(prefetch_count=prefetch)
        except Exception as e:
            print(e)
        else:
            return channel

    # 声明一个队列
    def queue_mq(self, channel, durable=True):
        try:
            result = channel.queue_declare(
                queue=self.__queue_name, durable=durable)
            print(result)
        except Exception as e:
            print(e)

    # 交换机队列绑定
    def exchange_queue(self, channel):
        try:
            channel.queue_bind(exchange=self.__exchange,
                               queue=self.__queue_name,
                               routing_key=self.__rout)
        except Exception as e:
            print(e)

    # 投递一条消息
    def message_mq(self, channel):
        try:
            result = channel.basic_publish(exchange=self.__exchange,
                                           routing_key=self.__rout,
                                           body=self.__message)
            if result:
                print(result)
        except Exception as e:
            print(e)

    # 创建一个直连交换机
    def direct_exchange(self, channel, exchange_name):
        try:
            channel.exchange_declare(exchange=exchange_name,
                                     exchange_type='direct')
        except Exception as e:
            print(e)

    # 创建一个主题交换机
    def topic_exchange(self, channel, exchange_name):
        try:
            channel.exchange_declare(exchange=exchange_name,
                                     exchange_type='topic',
                                     durable=False)
        except Exception as e:
            print(e)

    @staticmethod
    def open_data(filename):
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                data = f.read()
                return data
        except Exception as e:
            print(e)


# 测试时间装饰器
def test_nums(num=100):
    def test_time(func):
        def run(*args, **kwargs):
            time1 = time.time()
            for i in range(num):
                print(i)
                func(*args, **kwargs)
            time2 = time.time()
            print(num / (time2 - time1))
        return run
    return test_time


# 线程测试函数
def test_threads(func, nums, *args):
    for i in range(nums):   # 21*5/s
        print(i)
        thread = threading.Thread(target=func, args=args)
        thread.start()


# 在装饰器参数中加入需要测试的次数参数
# 测试创建1000条tcp连接的速度
@test_nums(100)
def test_tcp(client, parameters):
    client.connect_mq(parameters)


# 测试创建1000个信道的速度
@test_nums(100)
def test_channel(client, con):
    client.channel_mq(con)


# 测试声明1000个队列的速度
@test_nums(1000)
def test_queue(client, channel):
    client.queue_mq(channel)
    

# 测试声明1000个直连交换机的速度
@test_nums(100)
def test_exchange(client, channel, name):
    client.direct_exchange(channel, name)


# 测试声明1000个主题交换机的速度
@test_nums(100)
def test_topic_exchange(client, channel, name):
    client.topic_exchange(channel, name)
    
    
# 测试绑定交换机和队列的速度
@test_nums(100)
def test_exchange_bind_queue(client, channel):
    client.exchange_queue(channel)


# 测试投递消息的速度
@test_nums(100)
def test_message(client, channel):
    client.message_mq(channel)


if __name__ == '__main__':
    message = Client.open_data(FILENAME)
    client = Client(HOST, message)
    pars = client.add_pars()
    con = client.connect_mq(pars)
    channel = client.channel_mq(con)
    # client.direct_exchange(channel,'abc')
    # client.topic_exchange(channel,'topic_eeg')

    # 多线程测试#
    # 在test_threads函数中传递测试函数,线程数,函数参数。

    # 测试连接tcp的速度
    test_tcp(client, pars)   # 15/s
    test_threads(test_tcp, 5, client, pars)   # 21*5/s

    # 测试创建信道的速度
    # test_channel(client,con)    # 27/s
    # test_threads(test_channel, 5, client,con)   # 21*5/s

    # 测试创建队列的速度
    # test_queue(client,channel)    # 55000msg/s
    # test_threads(test_queue, 5, client,channel)   # 30000/s

    # 测试创建直连交换机的速度
    # test_exchange(client,channel,'direct_eeg')  # 单线程50msg/s
    # test_threads(test_exchange, 5, client,channel,'direct_eeg')    #
    # 多线程10*5msg/s

    # 测试创建主题交换机的速度
    # test_topic_exchange(client,channel,'topic_eeg')  # 50/s
    # test_threads(test_topic_exchange, 5, client,channel,'topic_eeg')    #
    # 多线程10*5msg/s

    # 测试绑定的速度
    # test_exchange_bind_queue(client,channel)  # 55/s
    # test_threads(test_exchange_bind_queue, 5, client,channel)    #
    # 多线程10*5msg/s

    # 测试投递消息的速度
    # test_message(client,channel)  # 12msg/s
View Code

 

 

测试结果

connection连接:单线程496/s,多线程最大750/s
信道创建:单线程800/s,多线程850/s;
队列创建:单线程3867/s,多线程3300/s
交换机创建:单线程3900/s,多线程3250/s
绑定创建:单线程2700/s,多线程2500/s
10字节消息投递速度:10300msg/s
300k消息传递:2400msg/s

 

总结

  • 可以看到信道的创建速度高于tcp连接,所以一般保持TCP连接而使用多个channel;

  • 对于业务来说,一般是客户端请求服务器提交数据,服务器连接rabbitmq存储数据,那么服务器可以先创建tcp长连接池或channel信道池,到可以提交数据后,服务器直接调用连接对象传递数据。

  • 队列和交换机一般是设置持久化的,它们可以长期存在,而消费者也是预先定义的,可以将队列的声明,交换机声明,绑定放在消费者端;

  • 生产和消费同时消耗rabbitmq的资源,当生产消费不平衡,如生产大于消费造成消息堆积时,消费者的消费速度回随着内存的减小变慢,可能造成性能的急剧恶化;

测试发现的一个问题

使用pika操作rabbitmq,在创建信道池后,如果一段时间rabbitmq的tcp连接没有接受到请求,其会强制关闭tcp连接,造成信道池不可用,所以需要重新开启TCP连接;

参考

 

posted on 2019-02-01 17:21  myworldworld  阅读(708)  评论(0)    收藏  举报

导航