在前面三节中,生产者只作为消息发送方,消费者只作为消息接收方。
假设生产者为客户端向队列中发送消息,服务器为消费者从队列中接收消息;
现在的需求时,生产者在发消息时,要求接收到服务器的返回结果,怎么办?
如果服务器将结果放在同一个队列中,那么在同一队列中既发送又接收消息,将形成死循环。
即在同一个队列中,任何一端都不能既作为生产者又作为消息者,只能选择一方,否则就是死循环。
解决办法:重新声明一个队列,来存放服务器返的结果;此时服务器作为生产者,客户端作为消费者。
这个过程,也称为RPC,Remote Producre Call远程过程调用。
但是,如果服务器不知道将任务结果放在哪个队列,客户端也不知道从哪个队列去取结果,怎么办?
解决办法:客户端在发送消息时,带上一个标记,如参数reply_to=任务结果的队列名称
假设,同一个客户端向任务器发送了三个耗时任务,要求服务器异步返回,在返回结果之前客户端可以做其它事,不需要一直等待。
解决办法:同上,除了为此客户端添加一个任务结果的队列标记外;
但是还需要分别为每个任务再加上一个新的标识符(消息区分),且实现异步返回。
即要求实现双生产、消费模式。如,客户端作为发送方发送任务,又作为接收方接收任务结果;
服务器既作为接收方接收任务,又作为发送方发送任务结果。 这就是RPC。
示例:
客户端:
import pika
import uuid
class FibonacciRpcClient(object):
def __init__(self):
self.connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))
self.channel = self.connection.channel()
# 生成一个随机queue作为任务结果的queue
result = self.channel.queue_declare(exclusive=True)
self.callback_queue = result.method.queue
# 作为接收端,准备接收任务结果
self.channel.basic_consume(self.on_response, no_ack=True,
queue=self.callback_queue)
def on_response(self, ch, method, props, body): # 作为接收方的callback函数
if self.corr_id == props.correlation_id:
self.response = body
def call(self, n):
self.response = None
self.corr_id = str(uuid.uuid4()) # 唯一标识符
# 作为发送者:发送消息及唯一标识符
self.channel.basic_publish(exchange='',
routing_key='rpc_queue',
properties=pika.BasicProperties(
reply_to=self.callback_queue,
correlation_id=self.corr_id,
),
body=str(n))
count = 0
while self.response is None:
# 作为接收方,检查队列中,有没有新消息,但不会阻塞即异步
self.connection.process_data_events()
count += 1
print("check....", count)
return int(self.response)
fibonacci_rpc = FibonacciRpcClient()
print(" [x] Requesting fib(30)")
response = fibonacci_rpc.call(30)
print(" [.] Got %r" % response)
服务器:
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='rpc_queue')
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
def on_request(ch, method, props, body):
n = int(body)
print(" [.] fib(%s)" % n)
response = fib(n)
# 作为发送方,发送消息
ch.basic_publish(exchange='',
routing_key=props.reply_to,
properties=pika.BasicProperties(correlation_id= \
props.correlation_id),
body=str(response))
ch.basic_ack(delivery_tag=method.delivery_tag)
# 公平分发:一次只接收prefetch_count个消息
channel.basic_qos(prefetch_count=1)
# 作为接收方,消费消息
channel.basic_consume(on_request, queue='rpc_queue')
print(" [x] Awaiting RPC requests")
channel.start_consuming()
参考:
celery连接rabbitMQ