Rabbitmq(6):RPC

引言

rabbitmq队列使用的公平分发,我们学习了如何使用工作队列在多个worker之间分配耗时的任务。

但是,如果我们需要在远程计算机上运行函数并等待结果怎么办?这种模式通常称为远程过程调用RPC。相关概念可参考rpc概述

在本教程中,我们将使用RabbitMQ构建一个RPC系统:客户端和可伸缩RPC服务器。由于我们没有值得分配的耗时任务,因此我们将创建一个虚拟RPC服务,该服务返回斐波那契数。

细节提要:服务端声明一个队列,接收消息,计算所对应的斐波那契数,然后根据消息的id信息,将结果发到客户端对应的回调队列中,并携带之前消息id,客户端从回调队列中获取结果。总结就是由C/S
结合rabbitmq的两个队列(服务端队列和客户端回调队列),完成rpc过程

回调队列

通常,通过RabbitMQ进行RPC很容易。客户端发送请求消息,服务器发送响应消息。为了接收响应,我们需要发送带有回调队列地址的请求。我们可以使用默认队列。

q, err := ch.QueueDeclare(
  "",    // 不指定队列名,默认使用随机生成的队列名
  false, // durable
  false, // delete when unused
  true,  // exclusive
  false, // noWait
  nil,   // arguments
)

err = ch.Publish(
  "",          // exchange
  "rpc_queue", // routing key 服务端声明的队列
  false,       // mandatory
  false,       // immediate
  amqp.Publishing{
   
    ContentType:   "text/plain",
    CorrelationId: corrId,
    ReplyTo:       q.Name,  // 在这里指定callback队列名,也是在这个队列等回复
    Body:          []byte(strconv.Itoa(n)),
})

消息属性

AMQP 0-9-1协议预定义了消息附带的14个属性集。除以下属性外,大多数属性很少使用:

  • persistent:将消息标记为持久性(值为true)或瞬态(false)。在rabbitmq队列使用开启消息持久化有提及
  • content_type:用于描述编码的mime类型。例如,对于经常使用的JSON编码,将此属性设置为application/json是一个好习惯。
  • reply_to:常用于命名回调队列
  • correlation_id:有助于将RPC响应与请求相关联

关联ID(Correlation Id)

在上面介绍的方法中,我们建议为每个RPC请求创建一个回调队列。这是相当低效的,但是幸运的是,有一种更好的方法——让我们为每个客户端创建一个回调队列

这就引发了一个新问题,在该队列中收到响应后,尚不清楚响应属于哪个请求。这个时候就该使用correlation_id这个属性了。针对每个请求我们将为其设置一个唯一值。随后,当我们在回调队列中收到消息时,我们将查看该属性,并基于这个属性将响应与请求进行匹配。如果我们看到未知的correlation_id值,则可以放心地丢弃该消息——它不属于我们的请求。

你可能会问,为什么我们应该忽略回调队列中的未知消息,而不是报错而失败?这是由于服务器端可能出现竞争状况。尽管可能性不大,但RPC服务器可能会在向我们发送答案之后但在发送请求的确认消息之前死亡。如果发生这种情况,重新启动的RPC服务器将再次处理该请求。这就是为什么在客户端上我们必须妥善处理重复的响应,并且理想情况下RPC应该是幂等的。

总结

在这里插入图片描述

我们的RPC工作流程如下:

  1. 客户端启动时,它将创建一个匿名排他回调队列。
  2. 对于RPC请求,客户端发送一条消息,该消息具有两个属性:reply_to(设置为回调队列)和correlation_id(设置为每个请求的唯一值)。
  3. 该请求被发送到rpc_queue队列。
  4. RPC工作程序(又名:服务器)正在等待该队列(rpc_queue)上的请求。当出现请求时,它会完成计算工作并把结果发送到消息replay_to字段中的队列。
  5. 客户端等待回调队列上的数据。出现消息时,它将检查correlation_id属性。如果它与请求中的值匹配,则将响应返回给应用程序。

完整示例

rpc服务端

rpc_server.go

package main

import (
	"log"
	"strconv"

	"github.com/streadway/amqp"
)

func main() {
   
	conn, err := amqp.Dial("amqp://root:123456@127.0.0.1:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值