0
0
Lập trình
Admin Team
Admin Teamtechmely

Xây dựng Redis Clone bằng Zig: Khám Phá Pub/Sub và Quản Lý Bộ Nhớ

Đăng vào 1 tháng trước

• 5 phút đọc

Giới thiệu

Dự án Zedis, một phiên bản Redis được viết bằng Zig, đã là một hành trình thú vị giúp tôi học hỏi về lập trình hệ thống cấp thấp. Trong bài viết này, tôi sẽ chia sẻ về hai tính năng cốt lõi mà tôi đã triển khai: Pub/Subchiến lược quản lý bộ nhớ.

Tại Sao Lại Xây Dựng Redis Clone Bằng Zig?

Tôi đã đặt ra mục tiêu cá nhân là làm chủ Zig trong năm nay, và việc xây dựng một dự án như thế này là cách hoàn hảo để đạt được điều đó. Zig mang lại nhiều tính năng hấp dẫn cho các hệ thống hiệu suất cao, chẳng hạn như comptime (cho phép mã chạy tại thời gian biên dịch) và quản lý bộ nhớ rõ ràng. Zedis đã trở thành sân chơi của tôi để khám phá mọi thứ từ lập trình mạng đến bộ cấp phát tùy chỉnh.

Khám Phá Tính Năng: Pub/Sub

Việc triển khai cơ chế Publish/Subscribe là một thử thách thú vị. Về cơ bản, đây là một hệ thống nhắn tin giúp tách biệt người gửi (nhà xuất bản) và người nhận (người đăng ký).

Cách Thức Hoạt Động Của Pub/Sub Trong Zedis

  • Kênh và Người Đăng Ký: Khi một client đăng ký vào một kênh, họ sẽ được thêm vào danh sách người đăng ký của kênh đó. Tôi đã sử dụng std.StringHashMap([]u64) để ánh xạ tên kênh với danh sách ID client.
  • Xuất Bản Một Tin Nhắn: Khi một tin nhắn được xuất bản đến một kênh, máy chủ sẽ duyệt qua danh sách người đăng ký và gửi tin nhắn đến mỗi kết nối của họ.
  • Chế Độ Pub/Sub: Khi một client đăng ký vào một kênh, họ sẽ vào chế độ “Pub/Sub” đặc biệt, nơi họ chỉ có thể nhận tin nhắn và không thể thực hiện các lệnh khác.

Ví Dụ Về Hàm subscribe

zig Copy
pub fn subscribe(client: *Client, args: []const Value) !void {
    var pubsub_context = client.pubsub_context;
    // Vào chế độ pubsub khi đăng ký lần đầu
    if (!client.is_in_pubsub_mode) {
        client.enterPubSubMode();
    }

    var i: i64 = 0;
    for (args[1..]) |item| {
        const channel_name = item.asSlice();
        // Đảm bảo kênh tồn tại
        pubsub_context.ensureChannelExists(channel_name) catch {
            try client.writeError("ERR failed to create channel");
            continue;
        };

        // Đăng ký client vào kênh
        pubsub_context.subscribeToChannel(channel_name, client.client_id) catch |err| switch (err) {
            error.ChannelFull => {
                try client.writeError("ERR maximum subscribers per channel reached");
                continue;
            },
            else => {
                try client.writeError("ERR failed to subscribe to channel");
                continue;
            },
        };

        const subscription_count = i + 1;
        const response_tuple = .{
            "subscribe",
            channel_name,
            subscription_count,
        };
        // Sử dụng writer chung để gửi tuple dưới dạng mảng RESP.
        try client.writeTupleAsArray(response_tuple);
        i += 1;
    }
}

Chiến Lược Quản Lý Bộ Nhớ

Một trong những khía cạnh thú vị nhất của dự án này là thiết kế chiến lược quản lý bộ nhớ. Tôi đã chọn một phương pháp lai để cân bằng giữa hiệu suất và việc sử dụng bộ nhớ:

  • KeyValueAllocator: Đây là một bộ cấp phát tùy chỉnh mà tôi xây dựng cho kho lưu trữ key-value chính. Nó sử dụng một vùng nhớ cố định và có chính sách loại bỏ đơn giản để giữ trong ngân sách của mình. Khi bộ cấp phát hết bộ nhớ, nó có thể loại bỏ tất cả các khóa để tạo không gian cho những khóa mới.
  • Bộ Cấp Phát Arena: Đối với các cấp phát tạm thời, ngắn hạn (như phân tích lệnh), tôi sử dụng bộ cấp phát arena. Điều này rất nhanh vì nó chỉ cần tăng một con trỏ cho các cấp phát mới và giải phóng toàn bộ bộ nhớ cùng một lúc khi không còn cần thiết.
  • Bể Cố Định: Đối với các đối tượng thường xuyên được cấp phát và giải phóng, như kết nối client, tôi sử dụng một bể kích thước cố định. Điều này giúp tránh chi phí của việc cấp phát và giải phóng động.

Phương pháp này cho phép Zedis sử dụng bộ nhớ hiệu quả trong khi vẫn duy trì hiệu suất cao.

Kế Hoạch Tương Lai

Tôi rất hào hứng để tiếp tục xây dựng trên nền tảng này. Dưới đây là những gì tôi dự định thực hiện:

  • Triển khai AOF (Append Only File) logging
  • Thêm hỗ trợ cho các cấu trúc dữ liệu khác như danh sách và tập hợp
  • Triển khai hết hạn khóa
  • Thêm hỗ trợ clustering

Khám Phá Zedis

Tôi rất muốn bạn kiểm tra Zedis trên GitHub, thử nghiệm và cho tôi biết suy nghĩ của bạn. Mọi đóng góp đều được hoan nghênh!

Cảm ơn bạn đã đọc!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào