首页 > 开源 > MsgTrans 2.0 Beta 1:把“高性能传输”升级为“可证明高性能可靠”

MsgTrans 2.0 Beta 1:把“高性能传输”升级为“可证明高性能可靠”

OSChina资讯 2026-08-29 12:10 4 阅读 查看原文

MsgTrans 是多协议高性能网络库,现在 MsgTrans 2.0 Beta 1 已经发布。

这是一次面向生产级实时网络的系统性重构,而不是在 1.x 上继续堆叠功能。

MsgTrans 继续通过统一 API 支持 TCP、WebSocket 和 QUIC,但 2.0 重新设计了事件传递、请求生命周期、写入确认、连接代际、资源限制、优雅关闭、协议扩展和公共 API。

我们的目标非常明确:

网络库不应只在正常环境里工作,还必须在弱网、拥塞、重连、超时、慢消费者和异常关闭下给出确定、可验证的结果。

一套业务代码,覆盖三种网络环境

MsgTrans 2.0 让业务层不再绑定具体协议。同一套消息、请求响应和会话处理逻辑,可以部署在三种传输之上。

| 协议 | 推荐场景 | 核心价值 |

|---|---|---|

| QUIC | 移动网络、弱网、高延迟、丢包环境 | 更适合互联网与低延迟传输 |

| TCP | 稳定内网、服务间通信、长连接网关 | 成熟、稳定、行为可预测 |

| WebSocket | 浏览器、HTTP 代理、防火墙限制、只放行普通 Web 流量的环境 | HTTP 基础设施兼容性更强 |

| 自定义协议 | 专用硬件、私有链路、定制传输 | 通过 msgtrans::spi 接入统一传输层 |

业务无需为不同协议维护三套请求状态机、会话生命周期和错误处理逻辑。

2.0 的核心变化:可靠性成为架构属性

1. 有界事件主干,不再静默丢消息

MsgTrans 2.0 删除了核心数据路径中的广播事件总线,改为每连接独立的有界事件管道和 Session Actor:


Connection → bounded channel → SessionActor → SessionHandler

慢消费者只会对自己的连接形成背压,不再影响其他连接。

数据、诊断和控制事件被拆分为不同平面:

  • 数据事件:有界、背压、不得丢弃

  • 诊断事件:允许在压力下丢弃,绝不挤占业务数据

  • 关闭事件:不排在数据队列后面,保证连接能够及时退出

旧广播主干在内部饱和测试中曾复现约 91% 的消息丢失。2.0 直接移除了这条结构性风险。

2. 请求生命周期只有一个真相源

2.0 使用统一的 Request Registry 管理请求状态:


Pending → Responding → Responded

                     → SendFailed

        → TimedOut

        → SessionClosed

        → Dropped

状态迁移使用原子操作,保证:

  • 一个请求最多只有一个响应者成功

  • 重复响应不会产生第二个网络包

  • 超时、断线和发送失败具有不同语义

  • 会话关闭时立即清理关联请求

  • 取消调用方 Future 不会留下永久 Pending 或 Responding

  • 迟到响应不会完成另一条新请求

Responder 携带不可伪造的 RequestToken,业务方无法手动构造消息 ID 或绕过请求注册表。

3. 彻底关闭重连 ABA 与旧连接污染

每次连接都会获得独立、单调递增的 Session ID。

连接和代际信息存储在同一个原子生命周期槽中。旧连接迟到的 Response、ConnectionClosed 或异步发送,只能影响旧代会话,无法触碰重连后的新连接。

请求 ID 由传输层按会话分配,并且拒绝回绕。宁可明确返回资源耗尽,也不会复用仍可能存在迟到响应的 ID 空间。

4. Ok 终于代表真实成功

2.0 不再把“进入发送队列”包装成“发送成功”。

所有正式 send 路径都是 write-confirmed:

let receipt = client.send(b"hello").await?;

只有底层写循环确认字节已经写入传输层,调用才返回成功。

如果只需要排队、不等待写入,可以显式使用:


client.send_detached(b"hello").await?;

两种语义通过命名直接区分,不再出现真假难辨的 Ok(())

请求超时现在返回真正的 Err,不再伪装成“成功但没有数据”。广播则返回 BroadcastReport,明确报告成功数量和每个会话的失败原因。

5. 可等待、可证明的确定性关闭

shutdown() 不再只是设置一个停止标记。

2.0 使用受控任务所有权和生命周期监督器,等待:

  • listener 停止

  • scanner 退出

  • actor 与事件泵结束

  • 会话资源释放

  • 连接许可全部归还

  • 监听端口真正释放

关闭结果通过 ShutdownReport 返回:

let report = server.shutdown().await?;



assert!(report.infra_stopped);

assert!(report.permits_restored);

assert!(report.clean);

这解决了 Ctrl+C 后 QUIC 或其他监听端口长时间不能重新绑定、幽灵会话占用连接配额、后台任务脱离服务器生命周期等问题。

严格、安全、统一的协议边界

Strict 成为默认策略

FramePolicy::Strict 现在是默认值。

以下输入都会被视为协议错误并关闭连接:

  • 未知 Packet Type

  • 未知 Compression Type

  • WebSocket 文本帧

  • 尾随多余字节

  • 非法固定头

  • 超过限制的扩展头或 Payload

  • 无法解压的数据

  • 声明长度与实际帧不一致

需要兼容 1.x 宽松行为时,仍可显式选择 FramePolicy::Lenient

三协议共享同一套尺寸限制

TCP、WebSocket、QUIC 不再各自维护不同的隐藏上限。

ServerLimits 和 ClientLimits 统一控制:

  • 最大 Payload

  • 最大扩展头

  • 最大帧尺寸

  • 写入截止时间

  • 事件管道容量

  • 出站队列容量

  • Actor 邮箱容量

同一个包不会再出现“WebSocket 可以发,TCP 却突然断开”的协议差异。

压缩不再可能制造损坏帧

压缩通过 SendOptions 和 RequestOptions 指定,由传输层在 Payload 完成后统一处理:

let options = SendOptions::new()

    .biz_type(7)

    .compression(CompressionType::Zstd);

如果构建时没有启用对应 codec,发送会明确失败,而不是把原始数据挂上“已压缩”标志继续发出。

入站解压只在共享事件边界执行一次。TCP、WebSocket、QUIC、客户端、服务端和自定义协议的上层消费者看到的始终是明文 Payload 与自洽的包头。

性能优化不是用可靠性换吞吐

MsgTrans 2.0 的性能方向不是盲目追求微基准数字,而是在稳定语义下减少热路径成本:

  • Bytes 驱动的 Payload 与零拷贝切片解码

  • 编码路径消除重复 Vec 和 freeze 拷贝

  • QUIC 读取缓冲减少无意义清零

  • 每连接独立 Actor,降低跨连接共享竞争

  • ConnectionWriter 将写路径从连接状态锁中分离

  • 等待队列和写入回执时不持有连接锁

  • 压缩和解压只执行一次

  • 广播选项在扇出前只准备一次

  • 有界队列防止慢连接无限吞噬内存

  • TCP、WebSocket、QUIC Cargo feature 真正裁剪协议依赖

当前三协议压力测试均保持零传输错误,架构重构前后未观察到显著吞吐回退。

更小、更诚实的公共 API

2.0 删除了大量“看起来能用、实际上未接线”或暴露内部实现的接口。

公共面现在只有两部分:

  • msgtrans::*:应用开发 API

  • msgtrans::spi::*:协议扩展 API

内部 Transport、Request Registry、Session Actor、协议适配器和状态机不再泄漏到公共命名空间。

公共 API 由快照纳入 CI。一旦接口发生变化,构建会直接失败,避免文档和实现静默漂移。

1.x 用户必须关注的升级点

MsgTrans 2.0 保持 wire format 不变,规范的 1.x 与 2.0 节点可以互通。但 Rust 公共 API 是有意进行的 breaking change。

SessionHandler 拆分

单向消息和请求不再混在同一个入口:

async fn on_message(

    &self,

    session_id: SessionId,

    packet: Packet,

    sender: SessionSender,

);



async fn on_request(

    &self,

    session_id: SessionId,

    packet: Packet,

    responder: Responder,

);

请求只能通过消费式 Responder 回应。

Packet 字段私有化


packet.message_id();

packet.biz_type();

packet.payload();

packet.into_payload();

packet.try_encode()?;

编码成为可失败操作,超限数据不会再被静默截断。

发送参数拆分

let send = SendOptions::new()

    .biz_type(7)

    .compression(CompressionType::Zstd);



let request = RequestOptions::new()

    .biz_type(7)

    .timeout(Duration::from_millis(500));

Raw Packet 发送入口已经删除,调用方无法再伪造 Request 类型和消息 ID。

WebSocket TLS 类型化

旧的 verify_tls: bool 被替换为:


ClientTls::SystemRoots

ClientTls::CustomCa(pem)

ClientTls::Insecure

每个选项都有真实实现,Insecure 只适合开发环境。

深层 import 迁移

应用类型统一从 crate root 导入:

use msgtrans::{Packet, ClientEvent, Responder};

协议实现者使用:

use msgtrans::spi::{Connection, ConnectionWriter, EventSink};

完整迁移说明:

适用场景

MsgTrans 2.0 尤其适合:

  • 即时通信、消息推送和实时协作

  • 移动网络与弱网传输

  • 游戏长连接和状态同步

  • 内网微服务通信

  • IoT 设备与网关

  • 同时面向 App、浏览器和后端服务的多协议网关

  • 需要请求响应、单向消息、广播与断线重连的实时系统

  • 需要自定义传输协议,但希望复用会话和请求生命周期的基础设施

如果项目只是普通 REST API,或者只需要短连接 HTTP 请求,MsgTrans 并不是 HTTP 框架的替代品。它面向的是持续连接、双向实时传输和可控生命周期。

如何开始

Beta 1 发布后可使用:

[dependencies]

msgtrans = {

    version = "2.0.0-beta.1",

    features = ["tcp", "websocket", "quic"]

}

启用压缩:

msgtrans = {

    version = "2.0.0-beta.1",

    features = ["tcp", "websocket", "quic", "zstd"]

}

对于现有 1.x 项目,建议采用滚动升级:

  1. 先迁移公共 API并保持原协议部署。

  2. 在测试环境启用 Strict 策略。

  3. 验证超时、重连、慢消费者与服务关闭场景。

  4. 对移动网络优先试运行 QUIC。

  5. 生产环境先灰度单协议,再扩展到多协议入口。

结语

MsgTrans 1.x 证明了统一 TCP、WebSocket 和 QUIC 是可行的。

MsgTrans 2.0 要证明的是另一件事:

在弱网、拥塞、异常输入、频繁重连和服务退出时,网络库仍然可以给出精确、确定、可观察的行为。

这不是一次简单升级,而是 MsgTrans 从多协议网络库迈向生产级实时传输基础设施的重要一步。