Jan 08, 2025
2 min read
Rust,
Axum,
MessagePack,

实战指南:如何在Rust中使用MessagePack优化HTTP接口的数据传输

MessagePack 是一种高效的二进制序列化格式。它支持多语言数据交换。相比JSON,它更紧凑,处理速度更快。

MessagePack 是一种高效的二进制序列化格式。它支持多语言数据交换。相比JSON,它更紧凑,处理速度更快。

MessagePack 数据体积小。这意味着传输时占用带宽少,速度快。与JSON相比,MessagePack 支持更多原始数据类型。包括8位到64位整数、浮点数、时间戳和自定义扩展类型。

尽管有这些优势,MessagePack 已存在十多年。许多资深开发者甚至没听说过它。这表明它的流行度较低。

主要优势

在笔者看来,MessagePack 的最大优势是支持原始二进制数据。

设想一个场景:你需要通过HTTP接口上传文件或图片数据。

通常有两种解决方案:

  • 第一,在请求体中直接放置二进制数据。
  • 第二,使用multipart/form-data作为传参协议。

如果还需要传递其他参数呢?按规范只能选择第二种方式。但如果你需要结构化的参数,multipart/form-data并不方便。虽然一些Web框架可以让其表现接近JSON,但处理流程不同。

这时,MessagePack 是个不错的选择。它允许你在请求体中包含类似JSON的数据,同时支持二进制数据。

使用方法

在 Rust 中,可以通过以下依赖使用:

[dependencies]
rmp-serde = "1.3.0"

假设有一个接口需要发送文本和二进制图片:

#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct Message {
    id: i32,
    text: String,
    image: Vec<u8>,
}

序列化代码如下:

fn main() {
    let img = include_bytes!("../img.png");

    let msg = Message {
        id: 1,
        text: "Hello, world!".to_string(),
        image: img.to_vec(),
    };
    let mut buf = Vec::new();
    msg.serialize(&mut Serializer::new(&mut buf)).unwrap();

    println!("{:?}", buf);
}

当传输 MessagePack 数据时,请求头 Content-Type 可以是:

  • application/msgpack
  • application/x-msgpack
  • application/*+msgpack

Web 框架中的应用

如果你使用 Axum 开发Rust项目,可以添加这个crate:

cargo add axum-msgpack

使用非常简单:

async fn post_handler(MsgPack(msg): MsgPack<Message>) -> Html<String> {
    let img = msg.image;
    // 更多操作
    "OK"
}

返回 MessagePack 数据也很容易:

async fn get_handler() -> MsgPack<Message> {
    let img = include_bytes!("../img.png");

    let msg = Message {
        id: 1,
        text: "Hello, world!".to_string(),
        image: img.to_vec(),
    };
    MsgPack(msg)
}

支持程度

由于 MessagePack 实现简单,几乎所有主流编程语言都支持它。详情可参考 这里

例如,在 JavaScript/TypeScript 中调用:

import { deepStrictEqual } from "assert";
import { encode, decode } from "@msgpack/msgpack";

const object = {
  id: 1,
  text: "Hello, world!",
  image: Uint8Array.from([1, 2, 3]),
};

const encoded: Uint8Array = encode(object);

// encoded 就可以放到 http body 里传输到后端。

deepStrictEqual(decode(encoded), object);