在分布式系统开发中,远程过程调用(RPC)是最基础也最重要的通信方式之一。传统RPC框架如gRPC、Web API等普遍采用单向请求-响应模型,这种设计存在几个明显的局限性:
典型的RPC调用流程可以简化为:
code复制客户端 -> 请求 -> 服务器
服务器 -> 响应 -> 客户端
这种模式下,通信的发起权完全掌握在客户端手中。服务器只能被动响应请求,无法主动向客户端发起调用。在实际工程实践中,这种限制会导致诸多不便:
以工业控制系统为例,常见的设备间通信需求包括:
传统RPC在这些场景下往往需要配合WebSocket、MQTT等额外协议使用,显著增加了系统复杂度和维护成本。
DmtpRpc采用了对等网络(Peer-to-Peer)架构,任何节点都可以同时充当客户端和服务器角色。这种设计带来了几个关键优势:
典型的通信模式示例:
code复制节点A <-> 节点B <-> 节点C
任何两个节点之间都可以直接进行RPC调用,无需经过中心节点转发。
DMTP(DotNet Message Transfer Protocol)是专为.NET生态设计的二进制协议,其帧结构如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| Head | 2字节 | 固定协议头0xAA,0xBB |
| Flag | 2字节 | 控制标志位 |
| Length | 4字节 | 数据部分长度 |
| Data | N字节 | 实际负载数据 |
协议特点:
与JSON/HTTP相比,DMTP在同等数据量下可减少30%-50%的网络传输量,特别适合高频率、低延迟的通信场景。
DmtpRpc采用接口优先的设计模式。首先定义服务契约:
csharp复制public interface IDeviceService
{
Task<DeviceStatus> GetStatus();
Task SetParameter(Parameter param);
[DmtpRpcMethod("紧急停止")] // 支持方法别名
Task EmergencyStop();
}
服务实现类:
csharp复制public class DeviceServiceImpl : IDeviceService
{
public Task<DeviceStatus> GetStatus()
{
// 实际业务逻辑
}
// 其他方法实现...
}
注册服务到RPC容器:
csharp复制var rpcStore = new RpcStore();
rpcStore.RegisterServer<IDeviceService, DeviceServiceImpl>();
建立TCP连接示例:
csharp复制// 服务端配置
var service = new TcpDmtpService();
service.Setup(new TouchSocketConfig()
.SetListenIPHosts(7789)
.ConfigureRpcStore(rpcStore));
// 客户端连接
var client = new TcpDmtpClient();
client.Setup(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7789"));
节点A调用节点B的服务:
csharp复制var deviceB = client.GetDmtpRpcActor().Invoke<IDeviceService>();
var status = await deviceB.GetStatus();
节点B也可以主动调用节点A:
csharp复制// 在节点B的代码中
var clientA = GetConnectedClient("A");
var serviceA = clientA.GetDmtpRpcActor().Invoke<IDeviceService>();
await serviceA.SetParameter(newParam);
DmtpRpc的协议无关设计使其可以运行在多种传输层上:
csharp复制// WebSocket传输
var wsService = new WebSocketDmtpService();
wsService.Setup(new TouchSocketConfig()
.SetListenIPHosts(7790));
// 命名管道传输
var pipeService = new NamedPipeDmtpService();
pipeService.Setup(new TouchSocketConfig()
.SetPipeName("MyPipe"));
csharp复制config.SetMaxCount(100) // 最大连接数
.SetIdleTimeout(TimeSpan.FromMinutes(5)); // 空闲超时
csharp复制// 批量调用示例
var tasks = new List<Task>();
for(int i=0; i<100; i++){
tasks.Add(deviceB.SetParameterAsync(params[i]));
}
await Task.WhenAll(tasks);
某汽车生产线改造项目采用DmtpRpc实现了:
相比原SOAP方案,网络流量降低62%,响应时间从平均120ms提升到28ms。
在智慧城市项目中,使用DmtpRpc构建了:
关键配置示例:
csharp复制// 边缘节点配置
config.SetRouteOptions(route => {
route.EnableRelay = true; // 启用中继功能
route.MaxRelayCount = 3; // 最大中继跳数
});
测试环境:
测试结果:
| 框架 | QPS | 平均延迟 | CPU占用 | 内存占用 |
|---|---|---|---|---|
| gRPC | 12,000 | 3.2ms | 78% | 450MB |
| WebAPI | 8,500 | 4.8ms | 65% | 380MB |
| DmtpRpc(TCP) | 18,000 | 2.1ms | 82% | 520MB |
| DmtpRpc(WebSocket) | 15,000 | 2.8ms | 75% | 490MB |
测试结论:
推荐采用统一的异常处理策略:
csharp复制try
{
var result = await client.Invoke<IService>(s => s.Method());
}
catch (RpcInvokeException ex) when (ex.Status == RpcStatus.Timeout)
{
// 处理超时
}
catch (RpcInvokeException ex)
{
// 处理其他RPC异常
}
catch (Exception ex)
{
// 系统级异常
}
csharp复制service.Setup(config => {
config.SetVerifyToken("MySecureToken");
config.SetRpcAuthHandler(client => {
// 自定义认证逻辑
return client.Id == "TrustedClient";
});
});
csharp复制config.SetSslOption(new SslOption {
Certificate = new X509Certificate2("cert.pfx"),
SslProtocols = SslProtocols.Tls12
});
内置的监控接口:
csharp复制// 获取RPC调用统计
var stats = rpcStore.GetRpcStatistic();
// 典型输出格式
/*
{
"TotalCalls": 1250,
"SuccessRate": 99.2%,
"AvgDuration": "23ms",
"LastError": null
}
*/
可能原因及解决方案:
优化建议:
csharp复制// 设置全局超时
config.SetRpcTimeout(TimeSpan.FromSeconds(30));
// 单个调用设置超时
client.Invoke<IService>(s => s.Method(), timeout: TimeSpan.FromSeconds(10));
处理方案:
DmtpRpc是TouchSocket项目的一部分,可与以下组件无缝集成:
与Dapr集成:
csharp复制// 在Dapr sidecar中托管
builder.Services.AddTouchSocketDmtp(option => {
option.ListenPort = 3000;
option.RegisterToDapr = true;
});
与Kubernetes服务发现:
csharp复制config.SetRemoteIPHostProvider(() => {
// 从K8s API获取服务端点
return GetServiceEndpoints("my-service");
});
根据社区反馈和实际需求,DmtpRpc正在规划以下增强:
在工业物联网项目中,我们发现当节点数超过500时,原生的路由发现机制会出现性能瓶颈。针对这种情况,我们开发了分级路由方案:
csharp复制config.SetRouteOptions(route => {
route.EnableZoneRouting = true;
route.ZoneLevels = 3;
route.LocalZone = "FactoryA/Line2";
});
这种设计将网络拓扑划分为多个层级,显著降低了广播风暴的风险。实测数据显示,在800节点的测试环境中,路由发现时间从原来的12秒降低到1.8秒。