NATS: ‘Cứu cánh’ cho Microservices khi Kafka và RabbitMQ quá nặng nề

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

Tại sao NATS lại ‘hot’ đến thế giữa rừng Message Broker?

Nếu anh em đang ‘vật lộn’ với cụm Kafka ngốn hàng chục GB RAM hay RabbitMQ cấu hình plugin rối rắm, NATS sẽ là một trải nghiệm hoàn toàn khác. Mình bén duyên với NATS khi refactor một hệ thống IoT. Lúc đó, server chỉ có 2GB RAM nhưng phải gánh hàng nghìn thiết bị gửi tin liên tục với độ trễ tối thiểu.

NATS được viết bằng Go và đóng gói gọn trong duy nhất một file binary. Triết lý của nó rất rõ ràng: “Đơn giản hóa thay vì phức tạp hóa”. Thay vì làm mọi thứ, NATS chỉ tập trung vào việc đẩy message đi với tốc độ nhanh nhất có thể.

So sánh thực tế: NATS vs RabbitMQ vs Kafka

Dưới đây là bảng tổng hợp từ kinh nghiệm thực chiến của mình khi triển khai cả 3 hệ thống này:

  • RabbitMQ (Đa năng nhưng cồng kềnh): Rất mạnh về routing phức tạp. Tuy nhiên, khi traffic tăng vọt, Erlang VM ngốn CPU rất khủng khiếp. Quản lý Cluster của RabbitMQ cũng là một thử thách lớn với dân DevOps.
  • Kafka (Xe tải hạng nặng): Khỏi bàn về khả năng lưu trữ hàng Terabyte dữ liệu. Thế nhưng, cài đặt Kafka kèm Zookeeper (hoặc KRaft) là ‘cực hình’ cho dự án nhỏ. Bạn cần ít nhất 1-2GB RAM chỉ để Broker có thể… thở.
  • NATS (Mô tô đua): File binary chỉ khoảng 15MB. Khởi động mất vài mili giây. RAM tiêu thụ chỉ tầm 20MB ở trạng thái nghỉ. Đây là lựa chọn số một cho Cloud Native và Edge Computing.

Con số thực tế: Mình từng thay cụm Kafka 3 node (mỗi node 4GB RAM) bằng NATS JetStream cho hệ thống log. Kết quả: throughput tương đương nhưng tài nguyên tiêu thụ giảm 90%. Đôi khi, nhỏ gọn mới là chân ái.

Triển khai NATS trong ‘một nốt nhạc’

Việc cài đặt NATS dễ đến mức bạn sẽ thấy bất ngờ. Anh em có thể chọn chạy Docker hoặc binary trực tiếp.

1. Chạy nhanh bằng Docker

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

Giải thích nhanh về Port:

  • Port 4222: Cổng giao tiếp chính cho Client.
  • Port 8222: Cổng HTTP để anh em check ‘sức khỏe’ hệ thống qua trình duyệt.

2. Sử dụng NATS CLI để test nhanh

Để bắn thử message mà không cần viết code, hãy cài nats tool. Trên Mac, bạn chỉ cần dùng Brew:

# Cài đặt nhanh
brew install nats-io/nats-tools/nats

# Test thử một message
nats pub my.subject "Hello NATS từ ITfromZero"

Cấu hình JetStream: Khi bạn cần lưu trữ dữ liệu

NATS có hai chế độ. Core NATS hoạt động theo kiểu ‘gửi và quên’ (fire-and-forget). Trong khi đó, JetStream giúp lưu trữ message xuống đĩa cứng giống như Kafka.

Tận dụng sức mạnh của Subject Hierarchies

NATS dùng dấu chấm . để phân cấp subject, giúp quản lý data cực thoáng. Ví dụ: orders.hanoi.created. Bạn có thể dùng wildcard để nhận tin linh hoạt:

  • orders.hanoi.*: Lấy mọi tin về đơn hàng tại Hà Nội.
  • orders.>: Lấy tất cả các tin bắt đầu bằng ‘orders’ không phân biệt khu vực.

Bật JetStream để chống mất dữ liệu

Mặc định NATS lưu dữ liệu trên RAM. Nếu server sập, message sẽ bay màu. Hãy tạo file server.conf để ép nó ghi xuống đĩa:

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

# Bảo mật bằng Token đơn giản
authorization {
    token: "secret-key-cua-ban"
}

Sau đó chạy server với lệnh: nats-server -c server.conf.

Code mẫu với Python (nats-py)

Đây là cách mình triển khai một Producer có sử dụng Request-Reply – một tính năng cực mạnh của NATS:

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

async def run():
    nc = NATS()
    # Kết nối kèm token bảo mật
    await nc.connect("nats://secret-key-cua-ban@localhost:4222")

    # Publish message thông thường
    await nc.publish("updates.itfromzero", b'NATS speed test')
    
    # Pattern Request-Reply: Gửi và chờ phản hồi ngay lập tức
    try:
        res = await nc.request("service.check", b'Status?', timeout=1)
        print(f"Phản hồi từ server: {res.data.decode()}")
    except asyncio.TimeoutError:
        print("Server bận rồi, không thấy phản hồi!")

    await nc.close()

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

Monitoring: Đừng để hệ thống chạy ‘mù’

Khi lên Production, việc giám sát là bắt buộc. NATS hỗ trợ công cụ nats-top, nhìn rất giống lệnh top trên Linux nhưng dành riêng cho message.

# Xem realtime throughput và kết nối
nats-top -s localhost:8222

Nếu dùng Prometheus, bạn chỉ cần thêm flag -m 8222. NATS sẽ tự phơi endpoint /metrics để bạn kéo dữ liệu về Grafana.

Mẹo nhỏ từ thực tế: Hãy chú ý đến Advisory Subjects. NATS sẽ tự bắn tin cảnh báo khi một Consumer bị ngắt kết nối đột ngột. Nhờ đó, mình thường xử lý được sự cố trước khi khách hàng kịp gửi ticket hỗ trợ.

3 lưu ý ‘xương máu’ khi dùng NATS

  1. Hạn chế dùng Wildcard ‘>’: Đừng subscribe vào tất cả mọi thứ nếu không thực sự cần. Nó sẽ khiến client của bạn ‘ngộp’ vì xử lý quá nhiều data rác.
  2. Dùng Queue Groups để Load Balance: Muốn chia tải cho 5 instance của một service? Hãy dùng queue groups. NATS sẽ tự động chia message theo kiểu Round-robin cực chuẩn.
  3. Kích thước message: NATS mặc định giới hạn 1MB. Nếu bạn định gửi file 50MB qua NATS, hãy dừng lại. Tốt nhất là đẩy file lên S3 và chỉ gửi link qua Broker.

NATS không phải là giải pháp thay thế hoàn toàn cho Kafka trong mọi bài toán dữ liệu lớn. Tuy nhiên, nếu bạn ưu tiên tốc độ, sự đơn giản và tiết kiệm chi phí hạ tầng, NATS chắc chắn là ứng viên số 1 hiện nay.

Share: