Polyconseil / aioamqp

AMQP implementation using asyncio
Other
280 stars 88 forks source link

rpc performance question #70

Closed appetito closed 8 years ago

appetito commented 8 years ago

hi!

i have modified rpc server aand client and they are very slow:

Client - sends requests in infitite loop:

#!/usr/bin/env python

"""
    RPC client, aioamqp implementation of RPC examples from RabbitMQ tutorial

"""

import asyncio
import uuid

import aioamqp

class FibonacciRpcClient(object):
    def __init__(self):
        self.transport = None
        self.protocol = None
        self.channel = None
        self.callback_queue = None
        self.waiter = asyncio.Event()

    @asyncio.coroutine
    def connect(self):
        """ an `__init__` method can't be a coroutine"""
        self.transport, self.protocol = yield from aioamqp.connect()
        self.channel = yield from self.protocol.channel()

        result = yield from self.channel.queue_declare(queue_name='', exclusive=True)
        self.callback_queue = result['queue']

        yield from self.channel.basic_consume(
            self.on_response,
            no_ack=True,
            queue_name=self.callback_queue,
        )

    @asyncio.coroutine
    def on_response(self, channel, body, envelope, properties):
        if self.corr_id == properties.correlation_id:
            self.response = body

        self.waiter.set()

    @asyncio.coroutine
    def call(self, n):
        if not self.protocol:
            yield from self.connect()
        self.response = None
        self.waiter.clear()
        self.corr_id = str(uuid.uuid4())
        yield from self.channel.basic_publish(
            payload=str(n),
            exchange_name='',
            routing_key='rpc_queue',
            properties={
                'reply_to': self.callback_queue,
                'correlation_id': self.corr_id,
            },
        )
        yield from self.waiter.wait()

        # yield from self.protocol.close()
        return self.response

@asyncio.coroutine
def rpc_client():
    fibonacci_rpc = FibonacciRpcClient()
    print(" [x] Requesting fib(30)")
    while True:
        pass
        response = yield from fibonacci_rpc.call(30)
        print(" [.] Got %r" % response)

asyncio.get_event_loop().run_until_complete(rpc_client())

Server - no calculations, just echo:

"""
    RPC server, aioamqp implementation of RPC examples from RabbitMQ tutorial
"""

import asyncio
import aioamqp

@asyncio.coroutine
def on_request(channel, body, envelope, properties):
    n = int(body)

    print(" [.] fib(%s)" % n)
    # response = fib(n)
    response = n

    yield from channel.basic_publish(
        payload=str(response),
        exchange_name='',
        routing_key=properties.reply_to,
        properties={
            'correlation_id': properties.correlation_id,
        },
    )

    yield from channel.basic_client_ack(delivery_tag=envelope.delivery_tag)

@asyncio.coroutine
def rpc_server():

    transport, protocol = yield from aioamqp.connect()

    channel = yield from protocol.channel()

    yield from channel.queue_declare(queue_name='rpc_queue')
    yield from channel.basic_qos(prefetch_count=1, prefetch_size=0, connection_global=False)
    yield from channel.basic_consume(on_request, queue_name='rpc_queue')
    print(" [x] Awaiting RPC requests")

event_loop = asyncio.get_event_loop()
event_loop.run_until_complete(rpc_server())
event_loop.run_forever()

And i have only 25 requests per second

Why so slow? why exactly 25?

Thanks

dzen commented 8 years ago

Hello @appetito,

this code is a sample on how to do some RPC using asyncio, but imho it's not good enough:

The client waits on yield from self.waiter.wait() so your code is not really async :(

The client call function should return an ayncio.Future() with the expected result, then you probably want to do an ayncio.wait([futures...]) that will return when all futures are completed.

dimabxsx commented 8 years ago

Hi @dzen, the problem is that the Nagle algorithm is enabled by default for socket. You can see another discussion here.

RemiCardona commented 8 years ago

Confirming. On my laptop, disabling Nagle boosts the performance of your sample clients from 25 req/s to about 1600 req/s. Definitely a nice improvement. Will push a fix ASAP.