NATS:KafkaやRabbitMQが重ますぎるときのMicroservicesの「救世主」

Development tutorial - IT technology blog
Development tutorial - IT technology blog

数あるメッセージブローカーの中で、なぜ今NATSが注目されているのか?

数十GBのRAMを消費するKafkaクラスターや、プラグイン設定が複雑なRabbitMQに苦労しているなら、NATSは全く新しい体験になるでしょう。私がNATSに出会ったのは、あるIoTシステムのリファクタリングをしていた時でした。当時、サーバーには2GBのRAMしかありませんでしたが、最小限の遅延で数千台のデバイスからの継続的なデータ送信を処理する必要がありました。

NATSはGo言語で書かれ、単一のバイナリファイルにパッケージ化されています。その哲学は明確です。「複雑にするのではなく、シンプルにする」こと。あらゆる機能を詰め込むのではなく、NATSはメッセージを可能な限り高速に配信することだけに集中しています。これは、プロジェクトが拡大しても保守性を保つクリーンアーキテクチャの考え方にも通じる重要な視点です。

実戦比較:NATS vs RabbitMQ vs Kafka

これら3つのシステムを実際に運用した経験に基づく比較をまとめました。マイクロサービス間のAPIの不整合を防ぎつつ、最適なツールを選ぶための参考にしてください:

  • RabbitMQ(多機能だが肥大化しやすい): 複雑なルーティングに非常に強いです。しかし、トラフィックが急増すると、Erlang VMのCPU消費が激しくなります。RabbitMQのクラスター管理も、DevOpsエンジニアにとっては大きな挑戦です。
  • Kafka(大型トラック): 数テラバイトのデータ蓄積能力については言うまでもありません。しかし、Zookeeper(またはKRaft)を含むKafkaのセットアップは、小規模プロジェクトにとっては「苦行」です。ブローカーが動作するためだけに、少なくとも1〜2GBのRAMが必要です。
  • NATS(レーシングバイク): バイナリサイズは約15MB。起動には数ミリ秒しかかかりません。アイドル時のRAM消費量はわずか20MB程度です。Cloud NativeやEdge Computingにとって、これはナンバーワンの選択肢です。

実際の数値: ログシステムにおいて、3ノードのKafkaクラスター(各ノード4GB RAM)をNATS JetStreamに置き換えたことがあります。結果として、スループットは同等ながら、リソース消費量を90%削減できました。時には、軽量であることこそが正義です。

「一瞬」で終わるNATSのデプロイ

NATSのインストールは驚くほど簡単です。Dockerを使用するか、バイナリを直接実行するかを選択できます。

1. Dockerでクイック起動

docker run -d --name nats-server -p 4222:4222 -p 8222:8222 nats:latest

ポートの簡単な説明:

  • ポート 4222: クライアント用のメイン通信ポート。
  • ポート 8222: ブラウザからシステムの「ヘルスチェック」を行うためのHTTPポート。

2. NATS CLIでクイックテスト

コードを書かずにメッセージを送信テストするには、nats ツールをインストールします。MacならBrewを使うだけです:

# クイックインストール
brew install nats-io/nats-tools/nats

# メッセージのテスト送信
nats pub my.subject "ITfromZeroからのHello NATS"

JetStreamの構成:データの永続化が必要な場合

NATSには2つのモードがあります。Core NATSは「投げっぱなし(fire-and-forget)」方式で動作します。一方、JetStreamはKafkaのようにメッセージをディスクに保存することを可能にします。より厳密な整合性が求められる場合は、Transactional Outbox Patternを併用してデータ損失を防ぐのが一般的です。

Subject Hierarchies(サブジェクト階層)の活用

NATSはドット . を使ってサブジェクトを階層化し、データを非常に見通しよく管理できます。例:orders.tokyo.created。ワイルドカードを使用して柔軟にメッセージを受信できます:

  • orders.tokyo.*: 東京での注文に関するすべてのメッセージを取得。
  • orders.>: 地域を問わず、「orders」で始まるすべてのメッセージを取得。

データ紛失を防ぐためのJetStream有効化

デフォルトではNATSはデータをRAMに保存します。サーバーがダウンするとメッセージは消えてしまいます。server.conf ファイルを作成して、ディスクに書き込むように強制しましょう:

# server.conf
jetstream {
    store_dir: "/data/nats-jetstream"
    max_mem: 1G
    max_file: 10G
}

# シンプルなトークン認証
authorization {
    token: "your-secret-key"
}

その後、次のコマンドでサーバーを実行します:nats-server -c server.conf

Pythonによる実装サンプル (nats-py)

これは、NATSの強力な機能であるRequest-Replyパターンを使用したプロデューサーの実装方法です:

import asyncio
from nats.aio.client import Client as NATS

async def run():
    nc = NATS()
    # セキュリティトークンを使用して接続
    await nc.connect("nats://your-secret-key@localhost:4222")

    # 通常のメッセージ送信(パブリッシュ)
    await nc.publish("updates.itfromzero", b'NATS speed test')
    
    # Request-Replyパターン:送信して即座に応答を待機
    try:
        res = await nc.request("service.check", b'Status?', timeout=1)
        print(f"サーバーからの応答: {res.data.decode()}")
    except asyncio.TimeoutError:
        print("サーバーが混雑しているか、応答がありません!")

    await nc.close()

if __name__ == '__main__':
    asyncio.run(run())

モニタリング:システムを「不透明」なままにしない

本番環境では監視が必須です。NATSは nats-top というツールをサポートしており、Linuxの top コマンドのようにメッセージ状況をリアルタイムで確認できます。

# リアルタイムのスループットと接続を確認
nats-top -s localhost:8222

Prometheusを使用している場合は、フラグ -m 8222 を追加するだけです。NATSは自動的に /metrics エンドポイントを公開し、Grafanaでデータを取得できるようになります。

現場からのヒント: Advisory Subjectsに注目してください。NATSは、コンシューマーが突然切断されたときなどに自動的に警告メッセージを送信します。また、複数のワーカーで処理が重複するのを防ぎたい場合は、Redis分散ロックを併用してレースコンディションを回避する設計も有効です。

NATSを運用する上での3つの重要な注意点

  1. ワイルドカード ‘>’ の多用を避ける: 本当に必要でない限り、すべてをサブスクライブしないでください。大量の不要なデータを処理することになり、クライアントがオーバーロードする原因になります。
  2. ロードバランスにはQueue Groupsを使う: サービスを5つのインスタンスで負荷分散したいですか? queue groups を使いましょう。NATSは自動的にラウンドロビン方式でメッセージを正確に分配します。
  3. メッセージサイズ: NATSのデフォルト制限は1MBです。もし50MBのファイルをNATS経由で送ろうとしているなら、考え直してください。ファイルをS3にアップロードし、ブローカー経由ではリンクのみを送るのが最善です。

NATSは、あらゆるビッグデータの問題においてKafkaを完全に置き換えるものではありません。しかし、スピード、シンプルさ、そしてEvent Sourcing & CQRSのような「過程」を重視するアーキテクチャの基盤として、NATSは間違いなく現在最高の候補です。

Share: