MessagePack
ライブラリ
MessagePack
概要
MessagePackは、効率的なバイナリシリアライゼーション形式です。JSONと同様の使いやすさを保ちながら、より高速でコンパクトなデータ表現を実現します。50以上の言語で実装され、高いポータビリティを誇り、IoT、モバイルアプリケーション、リアルタイム通信などでデータサイズの削減が重要な用途で継続的に選ばれています。特にゲーム開発、マイクロサービス、高頻度データ交換が求められる現代のアプリケーションで重要な役割を果たしています。
詳細
MessagePack 2025年版は、JSONと比較して30-50%のサイズ削減と10倍以上の処理速度向上を実現する成熟したバイナリ形式です。JSON形式の制約を排除し、バイナリデータの直接格納、非UTF-8エンコード文字列のサポート、任意の型のマップキー(配列や数値を含む)を可能にします。Redis、Pinterest、SignalRなどの大規模サービスで採用実績を持ち、エンタープライズ環境での信頼性が証明されています。
主な特徴
- コンパクト性: JSONより30-50%小さいデータサイズ
- 高速性: 10倍以上高速なシリアライゼーション・デシリアライゼーション
- 多言語対応: 50以上の言語での実装とクロスプラットフォーム互換性
- 型の柔軟性: バイナリデータ、非UTF-8文字列、任意型キーをサポート
- 実績: Redis、Pinterest等の大規模サービスでの採用実績
- 標準化: 明確な仕様と複数の実装による互換性
メリット・デメリット
メリット
- JSONと比較して大幅なデータサイズ削減(30-50%)と高速処理(10倍以上)
- 50以上の言語での実装により、マルチプラットフォーム環境での高い互換性
- バイナリデータと非UTF-8文字列の直接サポートでデータ表現の制約がない
- IoTデバイスやモバイル環境でのバッテリー消費とデータ通信費の削減
- ゲーム、金融取引、リアルタイム通信での実証済みパフォーマンス
- シンプルな仕様により実装の学習コストが低い
デメリット
- バイナリ形式のため人間による直接的な読み書きができない
- JSONと比較してツールやエディタサポートが限定的
- デバッグ時のデータ内容確認にはデコード処理が必要
- 配列や整数サイズに実装依存の制限がある
- 一部のライブラリでは機能や最適化レベルに差がある
- 小さなデータでは圧縮効果が限定的
参考ページ
書き方の例
基本的なセットアップ
# JavaScript/Node.js
npm install msgpack5
# または
npm install @msgpack/msgpack
# Python
pip install msgpack
# Java
# Maven
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack-core</artifactId>
<version>0.9.8</version>
</dependency>
# C# (.NET)
dotnet add package MessagePack
# Go
go get github.com/vmihailenco/msgpack/v5
# Rust
# Cargo.toml
[dependencies]
msgpack = "1.0"
JavaScript/Node.js での基本的な使用法
// @msgpack/msgpack を使用(推奨)
import { encode, decode } from '@msgpack/msgpack';
// 基本的なデータ構造
const userData = {
id: 123,
name: "田中太郎",
email: "[email protected]",
tags: ["admin", "user"],
metadata: {
createdAt: new Date(),
isActive: true,
balance: 1234.56
}
};
// エンコード(シリアライゼーション)
const encoded = encode(userData);
console.log('Encoded size:', encoded.length, 'bytes');
console.log('Encoded data:', encoded);
// デコード(デシリアライゼーション)
const decoded = decode(encoded);
console.log('Decoded data:', decoded);
// JSONとのサイズ比較
const jsonString = JSON.stringify(userData);
const jsonBytes = new TextEncoder().encode(jsonString);
console.log('JSON size:', jsonBytes.length, 'bytes');
console.log('MessagePack size:', encoded.length, 'bytes');
console.log('Size reduction:',
((jsonBytes.length - encoded.length) / jsonBytes.length * 100).toFixed(1), '%');
// バイナリデータの処理
const binaryData = {
filename: "document.pdf",
content: new Uint8Array([0x25, 0x50, 0x44, 0x46]), // PDF header
metadata: {
size: 1024,
type: "application/pdf"
}
};
const encodedBinary = encode(binaryData);
const decodedBinary = decode(encodedBinary);
console.log('Binary data preserved:', decodedBinary);
// 配列データの処理
const timeSeriesData = [];
for (let i = 0; i < 1000; i++) {
timeSeriesData.push({
timestamp: Date.now() + i * 1000,
value: Math.random() * 100,
status: i % 10 === 0 ? 'alert' : 'normal'
});
}
const encodedTimeSeries = encode(timeSeriesData);
console.log('Time series data encoded:', encodedTimeSeries.length, 'bytes');
Python での高度な使用法
import msgpack
import datetime
import numpy as np
from typing import Any, Dict, List
# カスタムオブジェクトのシリアライゼーション
class CustomObject:
def __init__(self, name: str, value: int):
self.name = name
self.value = value
self.created_at = datetime.datetime.now()
# カスタムエンコーダー
def custom_encoder(obj):
if isinstance(obj, CustomObject):
return {
'__type__': 'CustomObject',
'name': obj.name,
'value': obj.value,
'created_at': obj.created_at.isoformat()
}
elif isinstance(obj, datetime.datetime):
return {
'__type__': 'datetime',
'value': obj.isoformat()
}
elif isinstance(obj, np.ndarray):
return {
'__type__': 'numpy_array',
'dtype': str(obj.dtype),
'shape': obj.shape,
'data': obj.tobytes()
}
raise TypeError(f"Object of type {type(obj)} is not MessagePack serializable")
# カスタムデコーダー
def custom_decoder(obj):
if isinstance(obj, dict) and '__type__' in obj:
if obj['__type__'] == 'CustomObject':
custom_obj = CustomObject(obj['name'], obj['value'])
custom_obj.created_at = datetime.datetime.fromisoformat(obj['created_at'])
return custom_obj
elif obj['__type__'] == 'datetime':
return datetime.datetime.fromisoformat(obj['value'])
elif obj['__type__'] == 'numpy_array':
return np.frombuffer(
obj['data'],
dtype=obj['dtype']
).reshape(obj['shape'])
return obj
# 複雑なデータ構造
complex_data = {
'users': [
CustomObject('田中太郎', 100),
CustomObject('佐藤花子', 200)
],
'timestamp': datetime.datetime.now(),
'matrix': np.array([[1, 2, 3], [4, 5, 6]]),
'config': {
'debug': True,
'timeout': 30.5,
'features': ['auth', 'logging', 'metrics']
}
}
# エンコード
packed_data = msgpack.packb(complex_data, default=custom_encoder)
print(f"Packed size: {len(packed_data)} bytes")
# デコード
unpacked_data = msgpack.unpackb(packed_data, object_hook=custom_decoder, raw=False)
print(f"Unpacked data: {unpacked_data}")
# ストリーミング処理
def stream_processing():
# ストリーミングエンコード
packer = msgpack.Packer(default=custom_encoder)
# 大量データのストリーミング処理
stream_data = []
for i in range(10000):
item = {
'id': i,
'name': f'Item {i}',
'timestamp': datetime.datetime.now(),
'data': np.random.rand(10)
}
stream_data.append(packer.pack(item))
# 連結して一つのストリームに
combined_stream = b''.join(stream_data)
print(f"Stream size: {len(combined_stream)} bytes")
# ストリーミングデコード
unpacker = msgpack.Unpacker(object_hook=custom_decoder, raw=False)
unpacker.feed(combined_stream)
decoded_items = []
for item in unpacker:
decoded_items.append(item)
print(f"Decoded {len(decoded_items)} items from stream")
return decoded_items
stream_processing()
# パフォーマンス測定
import time
import json
def performance_benchmark():
# テストデータ生成
test_data = []
for i in range(10000):
test_data.append({
'id': i,
'name': f'User {i}',
'email': f'user{i}@example.com',
'active': i % 2 == 0,
'score': i * 1.5,
'tags': [f'tag{j}' for j in range(i % 5)]
})
# MessagePack エンコード
start_time = time.time()
msgpack_data = msgpack.packb(test_data)
msgpack_encode_time = time.time() - start_time
# MessagePack デコード
start_time = time.time()
msgpack_decoded = msgpack.unpackb(msgpack_data, raw=False)
msgpack_decode_time = time.time() - start_time
# JSON エンコード
start_time = time.time()
json_data = json.dumps(test_data).encode()
json_encode_time = time.time() - start_time
# JSON デコード
start_time = time.time()
json_decoded = json.loads(json_data.decode())
json_decode_time = time.time() - start_time
# 結果出力
print(f"MessagePack encode: {msgpack_encode_time:.4f}s")
print(f"MessagePack decode: {msgpack_decode_time:.4f}s")
print(f"MessagePack size: {len(msgpack_data)} bytes")
print(f"JSON encode: {json_encode_time:.4f}s")
print(f"JSON decode: {json_decode_time:.4f}s")
print(f"JSON size: {len(json_data)} bytes")
print(f"Encode speedup: {json_encode_time / msgpack_encode_time:.1f}x")
print(f"Decode speedup: {json_decode_time / msgpack_decode_time:.1f}x")
print(f"Size reduction: {(len(json_data) - len(msgpack_data)) / len(json_data) * 100:.1f}%")
performance_benchmark()
Java での使用法
// Maven依存関係を追加済みと仮定
import org.msgpack.core.MessageBufferPacker;
import org.msgpack.core.MessagePack;
import org.msgpack.core.MessageUnpacker;
import org.msgpack.value.Value;
import org.msgpack.value.Variable;
import java.io.IOException;
import java.util.*;
public class MessagePackExample {
// 基本的なシリアライゼーション
public static void basicSerialization() throws IOException {
// データの準備
Map<String, Object> userData = new HashMap<>();
userData.put("id", 123);
userData.put("name", "田中太郎");
userData.put("email", "[email protected]");
userData.put("active", true);
userData.put("balance", 1234.56);
userData.put("tags", Arrays.asList("admin", "user"));
Map<String, Object> metadata = new HashMap<>();
metadata.put("createdAt", System.currentTimeMillis());
metadata.put("version", "1.0");
userData.put("metadata", metadata);
// エンコード
MessageBufferPacker packer = MessagePack.newDefaultBufferPacker();
packer.packValue(MessagePack.newDefaultPacker().packValue(userData).build());
byte[] packed = packer.toByteArray();
packer.close();
System.out.println("Packed size: " + packed.length + " bytes");
// デコード
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(packed);
Value value = unpacker.unpackValue();
unpacker.close();
System.out.println("Unpacked value: " + value);
}
// 高性能なストリーミング処理
public static void streamingProcessing() throws IOException {
// 大量データのストリーミング書き込み
MessageBufferPacker packer = MessagePack.newDefaultBufferPacker();
// 配列の開始
packer.packArrayHeader(10000);
for (int i = 0; i < 10000; i++) {
packer.packMapHeader(4);
packer.packString("id").packInt(i);
packer.packString("name").packString("User " + i);
packer.packString("timestamp").packLong(System.currentTimeMillis());
packer.packString("active").packBoolean(i % 2 == 0);
}
byte[] packed = packer.toByteArray();
packer.close();
System.out.println("Stream packed size: " + packed.length + " bytes");
// ストリーミング読み込み
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(packed);
int arraySize = unpacker.unpackArrayHeader();
System.out.println("Processing " + arraySize + " items");
int processedCount = 0;
while (unpacker.hasNext() && processedCount < arraySize) {
int mapSize = unpacker.unpackMapHeader();
Map<String, Object> item = new HashMap<>();
for (int i = 0; i < mapSize; i++) {
String key = unpacker.unpackString();
Value value = unpacker.unpackValue();
item.put(key, value);
}
processedCount++;
// 100件ごとに進捗表示
if (processedCount % 100 == 0) {
System.out.println("Processed: " + processedCount + " items");
}
}
unpacker.close();
System.out.println("Total processed: " + processedCount + " items");
}
// バイナリデータの処理
public static void binaryDataHandling() throws IOException {
MessageBufferPacker packer = MessagePack.newDefaultBufferPacker();
// バイナリデータの作成
byte[] binaryData = new byte[1024];
for (int i = 0; i < binaryData.length; i++) {
binaryData[i] = (byte) (i % 256);
}
// バイナリデータを含むオブジェクト
packer.packMapHeader(3);
packer.packString("filename").packString("data.bin");
packer.packString("size").packInt(binaryData.length);
packer.packString("content").packBinaryHeader(binaryData.length);
packer.addPayload(binaryData);
byte[] packed = packer.toByteArray();
packer.close();
System.out.println("Binary data packed size: " + packed.length + " bytes");
// バイナリデータの読み込み
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(packed);
int mapSize = unpacker.unpackMapHeader();
String filename = null;
int size = 0;
byte[] content = null;
for (int i = 0; i < mapSize; i++) {
String key = unpacker.unpackString();
switch (key) {
case "filename":
filename = unpacker.unpackString();
break;
case "size":
size = unpacker.unpackInt();
break;
case "content":
int binarySize = unpacker.unpackBinaryHeader();
content = new byte[binarySize];
unpacker.readPayload(content);
break;
}
}
unpacker.close();
System.out.println("Filename: " + filename);
System.out.println("Size: " + size);
System.out.println("Content length: " + (content != null ? content.length : 0));
System.out.println("Binary data integrity: " +
(content != null && content.length == binaryData.length &&
Arrays.equals(content, binaryData) ? "OK" : "FAILED"));
}
public static void main(String[] args) {
try {
System.out.println("=== Basic Serialization ===");
basicSerialization();
System.out.println("\n=== Streaming Processing ===");
streamingProcessing();
System.out.println("\n=== Binary Data Handling ===");
binaryDataHandling();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ウェブアプリケーション統合とリアルタイム通信
// WebSocket + MessagePack でのリアルタイム通信
import { encode, decode } from '@msgpack/msgpack';
class MessagePackWebSocket {
constructor(url) {
this.ws = new WebSocket(url);
this.ws.binaryType = 'arraybuffer';
this.messageHandlers = new Map();
this.ws.onmessage = (event) => {
try {
const data = decode(new Uint8Array(event.data));
this.handleMessage(data);
} catch (error) {
console.error('Failed to decode MessagePack data:', error);
}
};
}
send(type, payload) {
if (this.ws.readyState === WebSocket.OPEN) {
const message = {
type,
payload,
timestamp: Date.now()
};
const encoded = encode(message);
this.ws.send(encoded);
}
}
on(messageType, handler) {
this.messageHandlers.set(messageType, handler);
}
handleMessage(data) {
const handler = this.messageHandlers.get(data.type);
if (handler) {
handler(data.payload);
}
}
}
// ゲームデータの高速同期
class GameStateSync {
constructor(websocket) {
this.ws = websocket;
this.gameState = {
players: new Map(),
entities: new Map(),
worldState: {}
};
// プレイヤー状態更新の処理
this.ws.on('player_update', (data) => {
this.updatePlayer(data);
});
// エンティティ状態更新の処理
this.ws.on('entity_update', (data) => {
this.updateEntity(data);
});
// 定期的な状態同期(60FPS)
setInterval(() => {
this.syncState();
}, 1000 / 60);
}
updatePlayer(playerData) {
this.gameState.players.set(playerData.id, {
position: { x: playerData.x, y: playerData.y, z: playerData.z },
rotation: { x: playerData.rx, y: playerData.ry, z: playerData.rz },
health: playerData.health,
timestamp: playerData.timestamp
});
}
updateEntity(entityData) {
this.gameState.entities.set(entityData.id, {
type: entityData.type,
position: entityData.position,
state: entityData.state,
timestamp: entityData.timestamp
});
}
syncState() {
// 変更された状態のみを送信(差分同期)
const changedPlayers = [];
const changedEntities = [];
// プレイヤーの変更を検出
this.gameState.players.forEach((player, id) => {
if (player.timestamp > Date.now() - 100) { // 100ms以内の変更
changedPlayers.push({ id, ...player });
}
});
// エンティティの変更を検出
this.gameState.entities.forEach((entity, id) => {
if (entity.timestamp > Date.now() - 100) {
changedEntities.push({ id, ...entity });
}
});
if (changedPlayers.length > 0 || changedEntities.length > 0) {
this.ws.send('state_sync', {
players: changedPlayers,
entities: changedEntities,
timestamp: Date.now()
});
}
}
}
// REST API での MessagePack サポート
class MessagePackAPI {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const headers = {
'Content-Type': 'application/msgpack',
'Accept': 'application/msgpack',
...options.headers
};
let body = options.body;
if (body && typeof body === 'object') {
body = encode(body);
}
const response = await fetch(url, {
...options,
headers,
body
});
if (response.headers.get('content-type')?.includes('application/msgpack')) {
const arrayBuffer = await response.arrayBuffer();
return decode(new Uint8Array(arrayBuffer));
} else {
return response.json();
}
}
async get(endpoint) {
return this.request(endpoint, { method: 'GET' });
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: data
});
}
}
// 使用例
const wsClient = new MessagePackWebSocket('wss://game.example.com/ws');
const gameSync = new GameStateSync(wsClient);
const api = new MessagePackAPI('https://api.example.com');
// API使用例
async function loadUserData() {
try {
const userData = await api.get('/users/123');
console.log('User data:', userData);
const updateResult = await api.post('/users/123', {
name: '田中太郎',
email: '[email protected]',
preferences: {
theme: 'dark',
notifications: true
}
});
console.log('Update result:', updateResult);
} catch (error) {
console.error('API error:', error);
}
}
loadUserData();