RabbitMQ RPC 实现
RPC,是 Remote Procedure Call 的简称,即远程过程调用。它是一种通过网络从远程计算机上请求服务,而不需要了解底层网络的技术。RPC 的主要功用是让构建分布式计算更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。
一般在 RabbitMQ 中进行 RPC 是很简单的。客户端发送请求消息,服务端回复响应的消息。为了客户端接收响应的消息,需要在请求消息中发送一个回调队列,在消息的 replyTo 属性中指定。为每个 RPC 请求创建一个回调队列是非常低效的,所以通常为每个客户端创建一个单一的回调队列。为了区分同一个回调队列中的不同请求,需要在消息的 correlationId 属性中设置当前请求的唯一识别ID。
RPC server 代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | package main import ( "fmt" "github.com/streadway/amqp" ) func main() { // connect to rabbitmq conn, err := amqp.Dial( "amqp://root:shiajun666@192.168.10.4:5672" ) if err != nil { fmt.Println( "Connect to RabbitMQ failed: " , err) return } defer conn.Close() // open a channel ch, err := conn.Channel() if err != nil { fmt.Println( "Open channel failed: " , err) return } defer ch.Close() // declare rpc request message queue _, err = ch.QueueDeclare( "rpc_queue" , //name true, //durable false, //autoDelete false, //exclusive false, //noWait nil, //args ) if err != nil { fmt.Println( "Declare rpc request message queue failed: " , err) return } // consume request messages reqCh, err := ch.Consume( "rpc_queue" , //queue "" , //consumer false, //autoAck false, //exclusive false, //noLocal false, //noWait nil, //args ) if err != nil { fmt.Println( "Consume request messages failed: " , err) return } // deal with requests go func () { for req := range reqCh { fmt.Printf( "Receive request[%s]: %s" , req.CorrelationId, string(req.Body)) // send response err = ch.Publish( "" , //exchange req.ReplyTo, //key false, //mandatory false, //immediate amqp.Publishing{ ContentType: "text/plain" , CorrelationId: req.CorrelationId, Body: []byte(fmt.Sprintf( "this is the response messsge for request %s" , req.CorrelationId)), }, ) // send ack req.Ack(false) } }() for { select {} } } |
RPC client 代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | package main import ( "fmt" "github.com/streadway/amqp" ) func main() { // connect to rabbitmq conn, err := amqp.Dial( "amqp://root:shiajun666@192.168.10.4:5672" ) if err != nil { fmt.Println( "Connect to RabbitMQ failed: " , err) return } defer conn.Close() // open a channel ch, err := conn.Channel() if err != nil { fmt.Println( "Open channel failed: " , err) return } defer ch.Close() // declare rpc response message queue q, err := ch.QueueDeclare( "" , //name true, //durable false, //autoDelete false, //exclusive false, //noWait nil, //args ) if err != nil { fmt.Println( "Declare rpc response message queue failed: " , err) return } // consume response messages respCh, err := ch.Consume( q.Name, //queue "" , //consumer false, //autoAck false, //exclusive false, //noLocal false, //noWait nil, //args ) if err != nil { fmt.Println( "Consume response messages failed: " , err) return } // send request to rpc request message queue corrId := "1234567890abcdefghigklmnopqrstuv" err = ch.Publish( "" , //exchange "rpc_queue" , //key false, //mandatory false, //immediate amqp.Publishing{ ContentType: "text/plain" , CorrelationId: corrId, ReplyTo: q.Name, Body: []byte(fmt.Sprintf( "this is the request message for request %s" , corrId)), }, ) // deal with responses go func () { for resp := range respCh { fmt.Printf( "Receive response for request[%s]: %s" , resp.CorrelationId, string(resp.Body)) // send ack resp.Ack(false) } }() for { select {} } } |
以上代码示例的大致流程为:
(1)RPC server 声明了一个名为 rpc_queue 的队列,用于存储 rpc 请求消息,该队列没有显式绑定交换器,直接使用 RabbitMQ 的默认交换器 "",然后调用 Channel.Consume() 开始接收请求消息;
(2)RPC client 声明了一个回调队列,队列名称由 RabbitMQ 随机生成,也是直接使用 RabbitMQ 的默认交换器 "",然后调用 Channel.Consume() 开始接收返回消息;
(3)RPC client 向请求队列 rpc_queue 发送请求消息,在消息的 replyTo 属性中设置回调队列的名称,在消息的 correlationId 属性中设置请求的唯一标识ID;
(4)RPC server 收到请求消息后,向消息的 replyTo 属性中指定的回调队列发送返回信息,在消息的 correlationId 属性中设置当前请求的唯一标识ID;
(5)RPC client 收到返回信息,根据消息中的 correlationId 属性将返回与请求对应起来。
注:
以上代码示例中,由于 RPC 请求消息队列只在 RPC server 中声明,故而应该先运行 RPC server 再运行 RPC client。RPC 请求消息队列和 RPC 响应消息队列都直接使用了 RabbitMQ 默认交换器,所以 Routing Key 为各自队列名称。
大致流程图:

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2019-09-30 go读写excel文件
2019-09-30 go读写文本文件
2019-09-30 排序算法