MessagePack for Go

高速で効率的なMessagePackシリアライゼーションライブラリ。標準のencoding/jsonパッケージと同様のAPIを提供し、最大5倍の性能向上を実現。

MessagePack for Go

概要

vmihailenco/msgpackは、Go言語向けの高速で効率的なMessagePackシリアライゼーションライブラリです。標準のencoding/jsonパッケージと同様のAPIを提供しながら、最大5倍の性能向上を実現します。オブジェクトプーリングやコンパクトエンコーディングなどの最適化により、メモリ効率と処理速度の両方で優れた性能を発揮します。

主な特徴

  1. 高性能: オブジェクトプーリングとメモリ管理の最適化により、JSONと比較して約5倍の高速化を実現
  2. 使いやすいAPI: encoding/jsonと同様のインターフェースで、既存コードからの移行が容易
  3. 拡張性: カスタムタイプのエンコード/デコード、MessagePack拡張機能のサポート
  4. 柔軟な設定: 構造体タグによるフィールド制御、配列エンコーディング、キーのソートなど
  5. 部分的デコード: クエリシステムによる特定データの効率的な抽出

インストール

go get github.com/vmihailenco/msgpack/v5

基本的な使い方

シンプルなエンコード/デコード

package main

import (
    "fmt"
    "github.com/vmihailenco/msgpack/v5"
)

type Item struct {
    ID   int    `msgpack:"id"`
    Name string `msgpack:"name"`
    Tags []string `msgpack:"tags,omitempty"`
}

func main() {
    // エンコード
    item := &Item{
        ID:   1,
        Name: "サンプルアイテム",
        Tags: []string{"タグ1", "タグ2"},
    }
    
    data, err := msgpack.Marshal(item)
    if err != nil {
        panic(err)
    }
    
    // デコード
    var decoded Item
    err = msgpack.Unmarshal(data, &decoded)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("デコード結果: %+v\n", decoded)
}

ストリーミングAPI

package main

import (
    "bytes"
    "github.com/vmihailenco/msgpack/v5"
)

func streamingExample() {
    buf := new(bytes.Buffer)
    
    // エンコーダーの作成
    enc := msgpack.NewEncoder(buf)
    enc.SetSortMapKeys(true) // マップキーをソート
    enc.UseCompactInts(true) // コンパクトな整数エンコーディング
    
    // 複数のオブジェクトをエンコード
    data := []interface{}{
        map[string]interface{}{"id": 1, "name": "item1"},
        map[string]interface{}{"id": 2, "name": "item2"},
    }
    
    for _, item := range data {
        if err := enc.Encode(item); err != nil {
            panic(err)
        }
    }
    
    // デコーダーの作成
    dec := msgpack.NewDecoder(buf)
    
    // ストリーミングデコード
    for {
        var item interface{}
        err := dec.Decode(&item)
        if err != nil {
            break
        }
        fmt.Printf("デコード: %v\n", item)
    }
}

高度な機能

カスタムエンコーディング

type CustomTime struct {
    time.Time
}

// カスタムエンコーダーの実装
func (ct CustomTime) EncodeMsgpack(enc *msgpack.Encoder) error {
    return enc.EncodeInt64(ct.Unix())
}

// カスタムデコーダーの実装
func (ct *CustomTime) DecodeMsgpack(dec *msgpack.Decoder) error {
    unix, err := dec.DecodeInt64()
    if err != nil {
        return err
    }
    ct.Time = time.Unix(unix, 0)
    return nil
}

MessagePack拡張機能

// カスタムタイプの拡張登録
func init() {
    msgpack.RegisterExt(1, (*CustomType)(nil))
}

type CustomType struct {
    Data string
}

func (ct *CustomType) MarshalMsgpack() ([]byte, error) {
    return []byte(ct.Data), nil
}

func (ct *CustomType) UnmarshalMsgpack(data []byte) error {
    ct.Data = string(data)
    return nil
}

クエリシステム

// 特定のフィールドのみを抽出
func queryExample(data []byte) {
    dec := msgpack.NewDecoder(bytes.NewReader(data))
    
    // "users.*.name"パスでユーザー名のみを抽出
    values, err := dec.Query("users.*.name")
    if err != nil {
        panic(err)
    }
    
    for _, name := range values {
        fmt.Printf("ユーザー名: %v\n", name)
    }
}

構造体の配列エンコーディング

type CompactUser struct {
    _msgpack struct{} `msgpack:",as_array"` // 配列としてエンコード
    ID       int
    Name     string
    Email    string
}

// または、エンコーダー全体で設定
enc := msgpack.NewEncoder(buf)
enc.UseArrayEncodedStructs(true)

設定オプション

構造体タグ

type User struct {
    ID        int      `msgpack:"id"`                    // フィールド名の指定
    Name      string   `msgpack:"name,alias:username"`   // エイリアスの設定
    Email     string   `msgpack:"email,omitempty"`       // 空の場合は省略
    Password  string   `msgpack:"-"`                      // エンコード対象外
    Tags      []string `msgpack:"tags,omitempty"`        // 空配列の場合は省略
    Metadata  map[string]interface{} `msgpack:",inline"` // インライン展開
}

エンコーダー設定

enc := msgpack.NewEncoder(writer)

// コンパクトな整数エンコーディング
enc.UseCompactInts(true)

// マップキーのソート
enc.SetSortMapKeys(true)

// 構造体を配列としてエンコード
enc.UseArrayEncodedStructs(true)

// カスタムタグの使用
enc.SetCustomStructTag("json")

// time.Timeの文字列エンコーディング
enc.SetOmitEmpty(true)

パフォーマンスベンチマーク

BenchmarkStructVmihailencoMsgpack-4   200000   12814 ns/op   2128 B/op    26 allocs/op
BenchmarkStructJSON-4                  20000   69438 ns/op   7864 B/op    26 allocs/op
BenchmarkStructGOB-4                   10000  104331 ns/op  14664 B/op   278 allocs/op

主な性能特性:

  • JSONと比較して約5.4倍高速
  • GOBと比較して約8.1倍高速
  • メモリ使用量が少ない(2128バイト vs JSONの7864バイト)
  • アロケーション回数が同等(26回)

実践的な使用例

APIレスポンスのシリアライゼーション

type APIResponse struct {
    Status  string      `msgpack:"status"`
    Code    int         `msgpack:"code"`
    Data    interface{} `msgpack:"data"`
    Message string      `msgpack:"message,omitempty"`
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
    response := &APIResponse{
        Status: "success",
        Code:   200,
        Data:   getUserData(),
    }
    
    data, err := msgpack.Marshal(response)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    w.Header().Set("Content-Type", "application/x-msgpack")
    w.Write(data)
}

メッセージキューでの使用

type Message struct {
    ID        string    `msgpack:"id"`
    Type      string    `msgpack:"type"`
    Timestamp time.Time `msgpack:"timestamp"`
    Payload   []byte    `msgpack:"payload"`
}

func publishMessage(queue MessageQueue, msg *Message) error {
    data, err := msgpack.Marshal(msg)
    if err != nil {
        return err
    }
    
    return queue.Publish(data)
}

func consumeMessage(data []byte) (*Message, error) {
    var msg Message
    err := msgpack.Unmarshal(data, &msg)
    return &msg, err
}

データストレージ

type CacheEntry struct {
    Key       string      `msgpack:"k"`
    Value     interface{} `msgpack:"v"`
    ExpiresAt time.Time   `msgpack:"e"`
}

func (c *Cache) Set(key string, value interface{}, ttl time.Duration) error {
    entry := &CacheEntry{
        Key:       key,
        Value:     value,
        ExpiresAt: time.Now().Add(ttl),
    }
    
    data, err := msgpack.Marshal(entry)
    if err != nil {
        return err
    }
    
    return c.store.Put(key, data)
}

他のシリアライゼーション形式との比較

JSON vs MessagePack

特性JSONMessagePack
可読性高い(テキスト形式)低い(バイナリ形式)
サイズ大きいコンパクト(20-50%削減)
速度標準的高速(約5倍)
型情報限定的豊富(整数型の区別など)
ブラウザサポートネイティブライブラリ必要

Protocol Buffers vs MessagePack

特性Protocol BuffersMessagePack
スキーマ必須(.proto)不要(スキーマレス)
型安全性高い中程度
柔軟性低い高い
サイズ最小小さい
開発速度遅い(スキーマ定義必要)速い

まとめ

vmihailenco/msgpackは、Go言語で高性能なシリアライゼーションが必要な場合の優れた選択肢です。標準的なAPIと豊富な機能により、JSONからの移行も容易で、マイクロサービス、リアルタイム通信、データストレージなど幅広い用途に適用できます。特に性能が重要なアプリケーションでは、その効率性が大きな利点となります。