在分布式系统开发中,RPC(远程过程调用) 是实现服务间通信的核心方式之一,而 RabbitMQ 作为一款成熟的消息队列中间件,不仅能实现异步消息投递,也能完美实现标准的 RPC 调用模式。
与传统的 HTTP RPC、gRPC 相比,基于 RabbitMQ 实现的 RPC 具备解耦、高可用、天然重试、负载均衡、异步非阻塞等核心优势,非常适合在分布式服务、跨进程通信、物联网设备指令调用等场景中落地。
本文将从原理到实战,完整讲解 RabbitMQ 实现 RPC 的核心机制,同时提供一套 符合 C# 编码规范、基于.NET 异步编程模型、可直接上生产环境 的完整 RabbitMQ RPC 封装代码,包含客户端 + 服务端全量实现,以及最佳实践和避坑指南。
一、RabbitMQ 实现 RPC 的核心原理
RabbitMQ 本身是异步的消息队列,但通过三大核心机制的组合,就能完美实现「客户端发起请求、同步等待响应、服务端处理并返回结果」的 RPC 核心诉求,这也是 RabbitMQ 官方文档中推荐的标准 RPC 实现方案。
✅ 核心实现三要素
1. CorrelationId(唯一请求标识,核心中的核心)
客户端发起每次 RPC 请求时,生成一个全局唯一的CorrelationId(通常用 Guid)并随消息一起发送;服务端处理完成后,原样带回该 ID 到响应消息中。客户端通过这个 ID 可以精准匹配「当前响应对应哪一次请求」,彻底解决响应错乱的问题,是实现 RPC 的基石。
2. ReplyTo(指定响应回调地址)
客户端发送请求时,通过消息属性ReplyTo指定「服务端处理完成后,将响应消息发送到哪个队列 / 路由键」;服务端从请求消息中读取该地址,处理完业务逻辑后,将响应结果发送到这个地址即可。
3. TaskCompletionSource(异步转同步等待)
这是.NET 中实现「异步等待结果」的核心类,客户端发送请求后,创建TaskCompletionSource对象并缓存,随后进入异步等待状态;当监听到对应CorrelationId的响应消息时,通过该对象唤醒等待并返回结果,全程无同步线程阻塞,高并发友好。
✅ RabbitMQ RPC 完整调用流程
- 客户端生成唯一
CorrelationId,创建TaskCompletionSource并缓存,封装请求消息(参数 + CorrelationId+ReplyTo)发送到请求队列; - 服务端监听请求队列,收到消息后执行业务处理逻辑,得到返回结果;
- 服务端从请求消息中读取
ReplyTo和CorrelationId,封装响应消息发送到指定的响应队列; - 客户端监听响应队列,匹配到对应
CorrelationId的响应消息后,唤醒等待的请求并返回结果; - 客户端清理本次请求的缓存对象,完成一次 RPC 调用。
二、核心特性说明(本次封装的 RPC 组件)
本次封装的RabbitMqRpcService是一套生产级的 RabbitMQ RPC 完整实现,基于.NET6 + 异步编程模型开发,完全遵循 C# 编码规范和 RabbitMQ 最佳实践,具备以下核心特性,开箱即用:✅ 客户端 + 服务端一体化封装,一套代码支持 RPC 调用与服务提供;✅ 基于业务编码businessCode实现多业务隔离,不同业务独立队列,互不干扰;✅ 完全异步编程模型,无同步阻塞,支持超高并发调用;✅ 完善的超时控制、异常捕获、日志记录,生产环境可追溯;✅ 通道 / 消费者复用机制,避免重复创建 RabbitMQ 资源,提升性能;✅ 严格实现 IDisposable 接口,资源自动释放,杜绝内存泄漏;✅ 支持 RabbitMQ 连接自动恢复、网络重连,高可用保障;✅ 手动 ACK 确认机制,消息零丢失,可靠性拉满;✅ 无侵入式设计,调用简单,耦合度极低。
三、完整封装代码(生产级可直接复制)
3.1 代码结构说明
- 命名空间:
SPC.AbpComponets.RPC.RabbitMQ - 核心类:
RabbitMqRpcService密封类,实现IRabbitMqRpcService接口,包含客户端 + 服务端全部逻辑; - 配置基类:
RabbitMqRpcConfigBase抽离 RabbitMQ 连接工厂配置,解耦配置逻辑; - 接口约束:
IRabbitMqRpcService规范对外暴露的 RPC 调用和服务启动方法。
3.2 全量完整代码
cs
using System;
using System.Collections.Concurrent;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Exceptions;
namespace SPC.AbpComponets.RPC.RabbitMQ
{
/// <summary>
/// RabbitMQ 实现的RPC远程调用服务(客户端+服务端一体化)
/// 遵循RabbitMQ官方RPC规范:CorrelationId+ReplyTo 实现请求响应绑定
/// 完全异步编程模型,支持业务隔离、自动重连、资源自动释放、超时控制、消息可靠投递
/// </summary>
public sealed class RabbitMqRpcService : RabbitMqRpcConfigBase, IRabbitMqRpcService, IDisposable
{
#region 常量定义 - 杜绝魔法字符串/魔法值,规范核心
private const string ExchangeRpcName = "rpc";
private const string RequestQueuePrefix = "rpc_request_queue_";
private const string ResponseQueuePrefix = "rpc_response_queue_";
private const string RequestRoutingKeyPrefix = "rpc_request_key_";
private const string ResponseRoutingKeyPrefix = "rpc_response_key_";
private const string DefaultBusinessCode = "DEFAULT";
private const int MinTimeoutSeconds = 1;
private const int MaxTimeoutSeconds = 300;
private const int ResponseMessageExpirationSeconds = 600;
private const int ResourceReleaseTimeoutMs = 1000;
#endregion
#region 私有字段 - 只读化+命名规范+按需私有化
private readonly IConnection _rabbitConnection;
private readonly ILogger<RabbitMqRpcService> _logger;
private readonly ConcurrentDictionary<string, IChannel> _channelCache = new ConcurrentDictionary<string, IChannel>();
private readonly ConcurrentDictionary<string, AsyncEventingBasicConsumer> _responseConsumerCache = new ConcurrentDictionary<string, AsyncEventingBasicConsumer>();
private readonly ConcurrentDictionary<string, AsyncEventingBasicConsumer> _serverConsumerCache = new ConcurrentDictionary<string, AsyncEventingBasicConsumer>();
private static readonly ConcurrentDictionary<string, TaskCompletionSource<string>> _pendingRequests = new ConcurrentDictionary<string, TaskCompletionSource<string>>();
private bool _isDisposed;
#endregion
/// <summary>
/// 构造函数 - 依赖注入初始化,禁止手动实例化
/// </summary>
/// <param name="configuration">配置文件</param>
/// <param name="logger">日志对象</param>
public RabbitMqRpcService(IConfiguration configuration, ILogger<RabbitMqRpcService> logger = null)
{
_logger = logger;
var connectionFactory = CreateConnectionFactory(configuration);
_rabbitConnection = CreateRabbitConnection(connectionFactory);
// 注册连接生命周期事件,主动释放资源
_rabbitConnection.ConnectionShutdown += (sender, args) => ReleaseAllResources();
_rabbitConnection.CallbackException += (sender, args) => _logger?.LogError(args.Exception, "RabbitMQ连接回调异常");
_rabbitConnection.ConnectionBlocked += (sender, args) => _logger?.LogWarning("RabbitMQ连接被阻塞:{0}", args.Reason);
}
#region 核心规范:严格实现IDisposable接口 - 资源释放标准写法
/// <inheritdoc />
public void Dispose()
{
if (_isDisposed) return;
ReleaseAllResources();
_isDisposed = true;
GC.SuppressFinalize(this);
}
/// <summary>
/// 释放所有RabbitMQ相关资源(连接、通道、消费者、等待任务)
/// 线程安全,幂等调用,可重复执行,无副作用
/// </summary>
private void ReleaseAllResources()
{
try
{
// 1. 释放所有缓存的通道
if (_channelCache.Count > 0)
{
foreach (var (cacheKey, channel) in _channelCache)
{
if (channel is not null && channel.IsOpen)
{
_ = channel.CloseAsync().WaitAsync(TimeSpan.FromMilliseconds(ResourceReleaseTimeoutMs));
_ = channel.DisposeAsync().AsTask().WaitAsync(TimeSpan.FromMilliseconds(ResourceReleaseTimeoutMs));
}
}
_channelCache.Clear();
}
// 2. 取消所有待处理的RPC请求,避免内存泄漏
if (_pendingRequests.Count > 0)
{
foreach (var tcs in _pendingRequests.Values)
{
_ = tcs.TrySetCanceled();
}
_pendingRequests.Clear();
}
// 3. 释放RabbitMQ主连接
if (_rabbitConnection is not null && _rabbitConnection.IsOpen)
{
_ = _rabbitConnection.CloseAsync().WaitAsync(TimeSpan.FromMilliseconds(ResourceReleaseTimeoutMs));
_rabbitConnection.Dispose();
}
_logger?.LogInformation("RabbitMQ RPC所有资源释放完成");
}
catch (Exception ex)
{
_logger?.LogError(ex, "RabbitMQ RPC资源释放过程中发生异常");
}
}
#endregion
#region 私有核心方法:获取RabbitMQ通道 - 复用+创建+队列交换机声明
/// <summary>
/// 获取指定业务编码的RabbitMQ通道,通道复用机制,不存在则创建并声明交换机/队列/绑定关系
/// </summary>
/// <param name="connection">RabbitMQ连接</param>
/// <param name="businessCode">业务隔离编码</param>
/// <returns>可用的RabbitMQ通道</returns>
private async Task<IChannel> GetChannelAsync(IConnection connection, string businessCode = "")
{
if (connection is null || !connection.IsOpen)
{
_logger?.LogError("RabbitMQ连接已关闭或为空,无法获取通道");
return null;
}
var cacheKey = string.IsNullOrWhiteSpace(businessCode) ? DefaultBusinessCode : businessCode;
// 通道复用:存在且可用则直接返回,提升性能
if (_channelCache.TryGetValue(cacheKey, out var existChannel) && existChannel.IsOpen)
{
return existChannel;
}
try
{
var channel = await connection.CreateChannelAsync();
// 声明持久化交换机:direct类型,RPC专用
await channel.ExchangeDeclareAsync(
exchange: ExchangeRpcName,
type: ExchangeType.Direct,
durable: true,
autoDelete: false);
// 声明请求队列+绑定路由键
var requestQueueName = $"{RequestQueuePrefix}{cacheKey}";
var requestRoutingKey = $"{RequestRoutingKeyPrefix}{cacheKey}";
await channel.QueueDeclareAsync(
queue: requestQueueName,
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
await channel.QueueBindAsync(requestQueueName, ExchangeRpcName, requestRoutingKey);
// 声明响应队列+绑定路由键
var responseQueueName = $"{ResponseQueuePrefix}{cacheKey}";
var responseRoutingKey = $"{ResponseRoutingKeyPrefix}{cacheKey}";
await channel.QueueDeclareAsync(
queue: responseQueueName,
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
await channel.QueueBindAsync(responseQueueName, ExchangeRpcName, responseRoutingKey);
// 缓存通道
_channelCache[cacheKey] = channel;
_logger?.LogDebug("RabbitMQ通道创建并缓存成功,业务编码:{0}", businessCode);
return channel;
}
catch (Exception ex)
{
_logger?.LogError(ex, "RabbitMQ通道创建失败,业务编码:{0}", businessCode);
return null;
}
}
#endregion
#region 私有核心方法:创建响应消息消费者 - 监听响应队列,匹配并唤醒RPC请求
/// <summary>
/// 创建并初始化响应消息消费者,持续监听指定队列的响应消息
/// </summary>
private async Task<AsyncEventingBasicConsumer> CreateResponseConsumerAsync(IChannel channel, string queueName)
{
if (channel is null || !channel.IsOpen || string.IsNullOrWhiteSpace(queueName))
{
_logger?.LogWarning("创建响应消费者失败:通道不可用或队列为空");
return null;
}
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.ReceivedAsync += async (sender, eventArgs) => await HandleResponseMessageAsync(channel, eventArgs);
// 启动消费:手动确认模式,确保消息可靠消费
await channel.BasicConsumeAsync(queue: queueName, autoAck: false, consumer: consumer);
return consumer;
}
/// <summary>
/// 处理响应队列的消息,匹配CorrelationId并唤醒对应的RPC请求
/// </summary>
private async Task HandleResponseMessageAsync(IChannel channel, BasicDeliverEventArgs eventArgs)
{
try
{
var messageBody = Encoding.UTF8.GetString(eventArgs.Body.ToArray());
var messageProps = eventArgs.BasicProperties;
var correlationId = messageProps.CorrelationId;
// 匹配并唤醒对应的RPC请求
if (!string.IsNullOrWhiteSpace(correlationId) && _pendingRequests.TryRemove(correlationId, out var tcs))
{
_ = tcs.TrySetResult(messageBody);
await channel.BasicAckAsync(eventArgs.DeliveryTag, multiple: false);
_logger?.LogDebug("RPC响应消息处理成功,CorrelationId:{0},响应内容:{1}", correlationId, messageBody);
}
else
{
// 无法匹配的消息,重新放回队列
await channel.BasicNackAsync(eventArgs.DeliveryTag, multiple: false, requeue: true);
_logger?.LogWarning("收到无法匹配的RPC响应消息,CorrelationId:{0}", correlationId);
}
}
catch (Exception ex)
{
// 消费异常,消息重新入队,避免丢失
await channel.BasicNackAsync(eventArgs.DeliveryTag, multiple: false, requeue: true);
_logger?.LogError(ex, "处理RPC响应消息时发生异常");
}
}
#endregion
#region 对外实现:RPC客户端调用 - 发送请求并异步等待响应(核心业务)
/// <inheritdoc />
public async Task<string> RpcClientAsync(string requestMessage, string businessCode = "", int timeoutSeconds = 60)
{
// 1. 入参合法性校验
if (string.IsNullOrWhiteSpace(requestMessage))
{
const string errorMsg = "RPC请求消息不能为空";
_logger?.LogWarning(errorMsg);
return $"error:{errorMsg}";
}
// 2. 超时参数兜底,防止非法值
timeoutSeconds = Math.Clamp(timeoutSeconds, MinTimeoutSeconds, MaxTimeoutSeconds);
var messageExpirationMs = timeoutSeconds * 1000;
var correlationId = Guid.NewGuid().ToString("N");
var cacheKey = string.IsNullOrWhiteSpace(businessCode) ? DefaultBusinessCode : businessCode;
var responseResult = string.Empty;
try
{
// 3. 获取可用通道
var channel = await GetChannelAsync(_rabbitConnection, businessCode);
if (channel is null || !channel.IsOpen)
{
const string errorMsg = "获取RabbitMQ通道失败,无法发送RPC请求";
_logger?.LogError(errorMsg);
return $"error:{errorMsg}";
}
// 4. 初始化响应消费者(单例,只创建一次)
if (!_responseConsumerCache.TryGetValue(cacheKey, out var consumer) || consumer.Model is null || !consumer.Model.IsOpen)
{
var responseQueueName = $"{ResponseQueuePrefix}{cacheKey}";
consumer = await CreateResponseConsumerAsync(channel, responseQueueName);
_responseConsumerCache[cacheKey] = consumer;
}
// 5. 构建请求消息属性
var requestProps = channel.CreateBasicProperties();
requestProps.Persistent = true;
requestProps.CorrelationId = correlationId;
requestProps.ReplyTo = $"{ResponseRoutingKeyPrefix}{cacheKey}";
requestProps.Expiration = messageExpirationMs.ToString();
// 6. 创建异步等待任务并缓存
var tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
if (!_pendingRequests.TryAdd(correlationId, tcs))
{
var errorMsg = $"RPC请求ID重复,创建等待任务失败,CorrelationId:{correlationId}";
_logger?.LogError(errorMsg);
return $"error:{errorMsg}";
}
// 7. 发送RPC请求消息
var requestRoutingKey = $"{RequestRoutingKeyPrefix}{cacheKey}";
var requestBody = Encoding.UTF8.GetBytes(requestMessage);
await channel.BasicPublishAsync(ExchangeRpcName, requestRoutingKey, mandatory: false, requestProps, requestBody);
_logger?.LogDebug("RPC请求发送成功,业务编码:{0},超时时间:{1}秒,CorrelationId:{2}", businessCode, timeoutSeconds, correlationId);
// 8. 异步等待响应,带超时控制
using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
var responseTask = tcs.Task;
await responseTask.WaitAsync(cancellationTokenSource.Token);
responseResult = responseTask.Result;
}
catch (OperationCanceledException)
{
var errorMsg = $"RPC请求超时,{timeoutSeconds}秒内未收到响应";
_logger?.LogWarning("{0},业务编码:{1},CorrelationId:{2}", errorMsg, businessCode, correlationId);
responseResult = $"timeout:{errorMsg}";
}
catch (BrokerUnreachableException ex)
{
var errorMsg = $"RabbitMQ服务不可达:{ex.Message}";
_logger?.LogError(ex, errorMsg);
responseResult = $"error:{errorMsg}";
}
catch (AlreadyClosedException ex)
{
var errorMsg = $"RabbitMQ通道已关闭:{ex.Message}";
_logger?.LogError(ex, errorMsg);
responseResult = $"error:{errorMsg}";
}
catch (Exception ex)
{
var errorMsg = $"RPC请求处理异常:{ex.Message}";
_logger?.LogError(ex, "{0},业务编码:{1}", errorMsg, businessCode);
responseResult = $"exception:{errorMsg}";
}
finally
{
// 无论成败,必须移除等待任务,杜绝内存泄漏
if (_pendingRequests.TryRemove(correlationId, out _))
{
_logger?.LogDebug("RPC请求任务已释放,CorrelationId:{0}", correlationId);
}
}
return responseResult;
}
#endregion
#region 对外实现:RPC服务端启动 - 监听请求队列并处理业务逻辑(核心业务)
/// <inheritdoc />
public async Task RpcServerAsync(Func<string, string> businessHandler, string businessCode = "")
{
// 入参校验:业务处理委托不能为空
if (businessHandler is null)
{
const string errorMsg = "RPC服务端业务处理委托不能为空";
_logger?.LogError(errorMsg);
throw new ArgumentNullException(nameof(businessHandler), errorMsg);
}
var cacheKey = string.IsNullOrWhiteSpace(businessCode) ? DefaultBusinessCode : businessCode;
// 服务端消费者复用:已存在则直接返回,避免重复创建
if (_serverConsumerCache.TryGetValue(cacheKey, out var existConsumer)
&& existConsumer.Model is not null
&& existConsumer.Model.IsOpen)
{
_logger?.LogDebug("RPC服务端消费者已存在,无需重复创建,业务编码:{0}", businessCode);
return;
}
// 获取可用通道
var channel = await GetChannelAsync(_rabbitConnection, businessCode);
if (channel is null || !channel.IsOpen)
{
_logger?.LogError("RPC服务端启动失败:获取RabbitMQ通道失败,业务编码:{0}", businessCode);
return;
}
var requestQueueName = $"{RequestQueuePrefix}{cacheKey}";
var consumer = new AsyncEventingBasicConsumer(channel);
// 注册请求消息处理事件
consumer.ReceivedAsync += async (sender, eventArgs) =>
await HandleRequestMessageAsync(channel, eventArgs, businessHandler);
// 启动消费:手动确认模式,确保消息可靠消费
await channel.BasicConsumeAsync(queue: requestQueueName, autoAck: false, consumer: consumer);
_serverConsumerCache[cacheKey] = consumer;
_logger?.LogInformation("RPC服务端启动成功,业务编码:{0},监听队列:{1}", businessCode, requestQueueName);
}
/// <summary>
/// 处理请求队列的消息,执行业务逻辑并返回响应结果
/// </summary>
private async Task HandleRequestMessageAsync(IChannel channel, BasicDeliverEventArgs eventArgs, Func<string, string> businessHandler)
{
try
{
var requestMessage = Encoding.UTF8.GetString(eventArgs.Body.ToArray());
var requestProps = eventArgs.BasicProperties;
var replyToRoutingKey = requestProps.ReplyTo;
var correlationId = requestProps.CorrelationId;
// 无回调地址,直接确认消息并丢弃
if (string.IsNullOrWhiteSpace(replyToRoutingKey))
{
await channel.BasicAckAsync(eventArgs.DeliveryTag, multiple: false);
_logger?.LogWarning("RPC请求无回调地址,已丢弃,CorrelationId:{0}", correlationId);
return;
}
// 执行业务逻辑
var responseMessage = businessHandler.Invoke(requestMessage);
_logger?.LogDebug("RPC请求处理完成,CorrelationId:{0},请求内容:{1},响应内容:{2}", correlationId, requestMessage, responseMessage);
// 构建响应消息属性
var responseProps = channel.CreateBasicProperties();
responseProps.Persistent = true;
responseProps.CorrelationId = correlationId;
responseProps.Expiration = ResponseMessageExpirationSeconds.ToString();
// 发送响应消息
var responseBody = Encoding.UTF8.GetBytes(responseMessage);
await channel.BasicPublishAsync(ExchangeRpcName, replyToRoutingKey, mandatory: false, responseProps, responseBody);
// 手动确认消息消费完成
await channel.BasicAckAsync(eventArgs.DeliveryTag, multiple: false);
}
catch (Exception ex)
{
// 业务处理异常,消息重新入队重试
await channel.BasicNackAsync(eventArgs.DeliveryTag, multiple: false, requeue: true);
_logger?.LogError(ex, "处理RPC请求消息时发生异常");
}
}
#endregion
#region 私有辅助方法:创建RabbitMQ连接 - 同步安全创建
/// <summary>
/// 安全创建RabbitMQ连接,异步转同步无死锁
/// </summary>
private IConnection CreateRabbitConnection(IConnectionFactory factory)
{
try
{
return Task.Run(async () => await factory.CreateConnectionAsync()).GetAwaiter().GetResult();
}
catch (Exception ex)
{
_logger?.LogError(ex, "RabbitMQ连接创建失败");
throw new InvalidOperationException("RabbitMQ连接初始化失败,请检查配置", ex);
}
}
#endregion
}
/// <summary>
/// RabbitMQ连接配置基类
/// </summary>
public abstract class RabbitMqRpcConfigBase
{
/// <summary>
/// 创建RabbitMQ连接工厂,配置自动重连、心跳等核心参数
/// </summary>
protected IConnectionFactory CreateConnectionFactory(IConfiguration configuration)
{
return new ConnectionFactory
{
HostName = configuration["RabbitMQ:HostName"] ?? "localhost",
Port = int.Parse(configuration["RabbitMQ:Port"] ?? "5672"),
UserName = configuration["RabbitMQ:UserName"] ?? "guest",
Password = configuration["RabbitMQ:Password"] ?? "guest",
VirtualHost = configuration["RabbitMQ:VirtualHost"] ?? "/",
DispatchConsumersAsync = true, // 必须开启:支持异步消费者
AutomaticRecoveryEnabled = true, // 自动恢复连接,高可用核心
NetworkRecoveryInterval = TimeSpan.FromSeconds(5), // 网络恢复间隔
RequestedHeartbeat = TimeSpan.FromSeconds(60) // 心跳检测,防止连接断开
};
}
}
/// <summary>
/// RabbitMQ RPC服务接口规范
/// </summary>
public interface IRabbitMqRpcService
{
/// <summary>
/// RPC客户端调用,发送请求并异步等待响应
/// </summary>
/// <param name="requestMessage">请求消息内容</param>
/// <param name="businessCode">业务隔离编码</param>
/// <param name="timeoutSeconds">超时时间(秒)</param>
/// <returns>响应消息内容</returns>
Task<string> RpcClientAsync(string requestMessage, string businessCode = "", int timeoutSeconds = 60);
/// <summary>
/// RPC服务端启动,监听指定业务的请求队列
/// </summary>
/// <param name="businessHandler">业务处理委托</param>
/// <param name="businessCode">业务隔离编码</param>
Task RpcServerAsync(Func<string, string> businessHandler, string businessCode = "");
}
}