【问题标题】:How to use FastAPI as consumer for RabbitMQ (RPC)如何使用 FastAPI 作为 RabbitMQ (RPC) 的消费者
【发布时间】:2021-01-05 21:31:22
【问题描述】:

示例here 展示了如何使用远程过程调用 (RPC) 在 python 中创建客户端和服务器。

但我无法想象 FastAPI 服务如何成为使用 pika for RabbitMQ 使用来自 RCP 客户端的请求的服务器。

将通过显式调用它们来请求任何 Web 服务,但是,我无法想象如何将 RabbitMQ 消费者集成到 Web 服务中。

另一方面,对于客户端来说,这样做很容易,通过显式调用 Web 服务,您可以发布队列请求,see this example

有什么帮助吗?还是一个好的开始?

【问题讨论】:

  • 我不确定您的申请将走向何方。恕我直言 API 端点是等待请求并向客户端提供答案的“被动”应用程序,而队列/流消费者通常是不同的用例,始终侦听消息然后对数据执行某些操作而无需“回复”。我并不是说这两件事不能同时进行,但如果没有关于你目标的更多细节,我很难完全理解如何回答。

标签: web-services flask rabbitmq fastapi pika


【解决方案1】:

您可以使用 aio_pikaRPC 模式并执行以下操作:

服务 1(消耗)

循环消费:

# app/__init__.py

from fastapi import FastAPI
from app.rpc import consume

app = FastAPI()

...

@app.on_event('startup')
def startup():
    loop = asyncio.get_event_loop()
    # use the same loop to consume
    asyncio.ensure_future(consume(loop))

...

创建连接、通道和注册远程方法以从另一个服务调用:

# app/rpc.py

from aio_pika import connect_robust
from aio_pika.patterns import RPC

from app.config import config

__all__ = [
    'consume'
]


def remote_method():
    # DO SOMETHING
    # Move this method along with others to another place e.g. app/rpc_methods
    # I put it here for simplicity
    return 'It works!'

async def consume(loop):
    connection = await connect_robust(config.AMQP_URI, loop=loop)
    channel = await connection.channel()
    rpc = await RPC.create(channel)

    # Register your remote method
    await rpc.register('remote_method', remote_method, auto_delete=True)
    return connection

这就是您需要使用和响应的全部内容,现在让我们看看调用此远程方法的第二个服务。

服务 2(调用远程方法)

让我们首先创建 RPC 中间件,以便轻松管理和访问 RPC 对象以从 API 函数调用我们的远程方法:

# app/utils/rpc_middleware.py

import asyncio

from fastapi import Request, Response

from aio_pika import connect_robust
from aio_pika.patterns import RPC

from app.config import config

__all__ = [
    'get_rpc',
    'rpc_middleware'
]


async def rpc_middleware(request: Request, call_next):
    response = Response("Internal server error", status_code=500)
    try:
        # You can also pass a loop as an argument. Keep it here now for simplicity
        loop = asyncio.get_event_loop()
        connection = await connect_robust(config.AMQP_URI, loop=loop)
        channel = await connection.channel()
        request.state.rpc = await RPC.create(channel)
        response = await call_next(request)
    finally:

        # UPD: just thought that we probably want to keep queue and don't
        # recreate it for each request so we can remove this line and move
        # connection, channel and rpc initialisation out from middleware 
        # and do it once on app start

        # Also based of this: https://github.com/encode/starlette/issues/1029
        # it's better to create ASGI middleware instead of HTTP
        await request.state.rpc.close()
    return response


# Dependency to use rpc inside routes functions
def get_rpc(request: Request):
    rpc = request.state.rpc
    return rpc

应用 RPC 中间件:

# app/__init__.py

from app.utils import rpc_middleware

...

app.middleware('http')(rpc_middleware)

...

在 API 函数中通过依赖使用 RPC 对象:

# app/api/whatever.py

from aio_pika.patterns import RPC

from app.utils import get_rpc

...

@router.get('/rpc')
async def rpc_test(rpc: RPC = Depends(get_rpc)):
    response = await rpc.proxy.remote_method()
    ...

添加一些日志记录以跟踪两个服务中发生的情况。您还可以将两个服务中的 RPC 逻辑合并为一个,以便能够从同一个服务中使用和调用远程方法。

希望它有助于获得基本的想法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 2013-10-10
    • 1970-01-01
    • 2013-03-25
    • 1970-01-01
    相关资源
    最近更新 更多