Mô Hình Socket.channel Mới Của Skynet - nói dối e blog

Mô Hình Socket.channel Mới Của Skynet

Hầu hết các dịch vụ mạng bên ngoài đều hoạt động theo cơ chế yêu cầu-phản hồi. Khi tích hợp Skynet với các cơ sở dữ liệu bên ngoài, việc sử dụng trực tiếp các API socket để viết driver thường gặp nhiều phức tạp. Nhà phát triển phải giải quyết bài toán đọc phản hồi bất đồng bộ, đồng thời xử lý logic tái kết nối khi mất kết nối xảy ra - vốn là công việc dễ gây sai sót.

Trong hai ngày cuối tuần vừa qua, tôi đã thêm vào module socket của Skynet một mô hình mới có tên “channel”, giúp đơn giản hóa quy trình xử lý các vấn đề nói trên. Với cú pháp socket.channel { host = hostname, port = port_number }, chúng ta có thể tạo ra một đối tượng channel chuyên dụng cho việc truyền tín hiệu với máy chủ từ xa.

Cơ chế xử lý cho giao thức Redis

Đặc trưng của giao thức Redis là mỗi yêu cầu gửi đi sẽ tương ứng với một gói phản hồi nhận về theo đúng thứ tự hàng đợi. Trong trường hợp này, API channel:request(request, response) sẽ phát huy hiệu quả tối đa. Tham số request là chuỗi dữ liệu cần gửi, còn response là một hàm callback có khả năng trả về hai giá trị:

  • Giá trị boolean đầu tiên xác nhận tính hợp lệ của phản hồi (true = thành công)
  • Giá trị thứ hai chứa nội dung phản hồi đã được phân tích

Nếu hàm response trả về false, giá trị thứ hai sẽ là đối tượng lỗi. Lúc này channel:request sẽ ném ra một exception để xử lý lỗi tập trung.

Cơ chế xử lý cho giao thức MongoDB

MongoDB áp dụng mô hình định danh phiên (session ID) thay vì đảm bảo thứ tự yêu cầu-phản hồi. Điều này có nghĩa là phản hồi cho yêu cầu A có thể đến sau phản hồi của yêu cầu B. Để xử lý, khi khởi tạo channel, chúng ta cần cung cấp một hàm phân tích phản hồi:

1
2
3
4
5
6
7
socket.channel { 
    host = hostname, 
    port = port_number,
    response = function(pkg) 
        -- Trả về session_id, success, data
    end
}

Hàm này phải trả về ba giá trị:

  1. session_id: Xác định phiên yêu cầu tương ứng
  2. success: Trạng thái xử lý (true/false)
  3. data: Dữ liệu kèm theo

Khi gửi yêu cầu, chúng ta sử dụng channel:request(request, session) với tham số thứ hai là ID phiên. Đặc biệt, với các yêu cầu không cần phản hồi (thường thấy trong giao thức MongoDB), chỉ cần gọi channel:request(request). Nếu sau đó cần nhận phản hồi trễ, có thể dùng channel:response(response) - cơ chế này đang được ứng dụng để triển khai mô hình pubsub của Redis.

Tự động xử lý lỗi và xác thực

Channel hiện nay có khả năng tự động bắt lỗi trong quá trình xử lý phản hồi, bao gồm cả các sự cố ngắt kết nối mạng. Khi phát hiện lỗi, hệ thống sẽ:

  1. Ngắt kết nối hiện tại
  2. Thử kết nối lại máy chủ
  3. Gửi lại các yêu cầu chưa nhận được phản hồi

Nếu dịch vụ yêu cầu xác thực trước khi kết nối, bạn có thể cung cấp hàm auth khi khởi tạo channel. Hàm này sẽ được gọi tự động sau mỗi lần thiết lập kết nối thành công.

Ưu điểm vượt trội

Việc áp dụng mô hình channel giúp loại bỏ hoàn toàn cơ chế socket.lock vốn dễ gây deadlock. Nhờ xử lý hoàn toàn trong coroutine, các yêu cầu được thực thi hoàn toàn không đồng bộ mà không làm nghẽn hệ thống. Điều này khiến mô hình batch truyền thống của Redis driver trở nên lỗi thời - phiên bản mới đã loại bỏ hoàn toàn tính năng này.

Gợi ý thử nghiệm

Hiện tại tôi đã commit phiên bản mới lên nhánh channel để các bạn có thể thử nghiệm. Đặc biệt mong các bạn đang sử dụng MongoDB driver của Skynet tham gia kiểm tra tính ổn định của phiên bản mới. Những đóng góp của các bạn sẽ giúp hoàn thiện mô hình này trước khi hợp nhất vào nhánh chính.

0%