Sử Dụng Ffi Của Luajit Để Kết Nối Zeromq - nói dối e blog

Sử Dụng Ffi Của Luajit Để Kết Nối Zeromq

Cách sử dụng thư viện ffi của luajit để kết nối zeromq

Cộng đồng phát triển ngôn ngữ Lua đang có nhiều hoạt động sôi nổi trong thời gian gần đây. Phiên bản Lua 5.2.0 (beta-rc2) vừa được công bố vào ngày 22 tháng 6, và hôm nay (24 tháng 6) là phiên bản LuaJIT-2.0.0-beta8. Dù vẫn còn một số khác biệt nhỏ giữa luajit và Lua 5.2, nhưng việc luajit chưa hỗ trợ đầy đủ Lua 5.2 không phải là vấn đề lớn. Cả hai đều mang lại nhiều lựa chọn thú vị cho cộng đồng phát triển. Người dùng có thể cân nhắc giữa việc sử dụng phiên bản chính thức 5.2 với hiệu năng ổn định hoặc chọn luajit2 để tận dụng tốc độ xử lý vượt trội. Cá nhân tôi ưu tiên hiệu năng nên đã chọn luajit làm công cụ chính. Đặc biệt, thư viện ffi mà luajit2 cung cấp thực sự là một công cụ mạnh mẽ, giúp đơn giản hóa đáng kể việc xây dựng các lớp kết nối (binding) giữa Lua và C. Điều này cũng làm nổi bật tầm quan trọng của việc thiết kế các giao diện C chuẩn mực cho các module hạ tầng, thay vì sử dụng C++.

ZeroMQ là một thư viện mạng được viết bằng C++, nhưng lại cung cấp một giao diện C thuần túy và gọn nhẹ. Chính đặc điểm này đã tạo điều kiện thuận lợi cho việc tích hợp với nhiều ngôn ngữ lập trình khác. Trên thực tế, đã tồn tại thư viện lua-zmq ổn định với hai phiên bản hỗ trợ ffi và không hỗ trợ ffi. Tuy nhiên, lớp bao bọc (wrapper) của thư viện này còn khá phức tạp. Nếu chỉ tập trung vào phiên bản ffi, chúng ta có thể xây dựng một giải pháp gọn nhẹ và hiệu quả hơn nhiều.

Với mục tiêu thực hành thư viện ffi của luajit và tạo ra một mã nguồn sạch sẽ, tôi đã dành khoảng nửa buổi chiều để tự xây dựng lớp kết nối zeromq. Thời gian này thậm chí còn ít hơn việc cấu hình cài đặt lua-zmq sẵn có trên Windows (không cần cài đặt msys, cmake và các công cụ phụ thuộc phức tạp). Dù có người cho rằng việc này là “sáng tạo lại bánh xe”, nhưng tôi cho rằng thời gian tranh luận còn tốn kém hơn viết mã. Việc xây dựng lớp kết nối C bằng ffi thực sự đơn giản không kém gì chương trình “hello world”.

Để khuyến khích các bạn tự xây dựng giải pháp riêng, tôi sẽ không công bố toàn bộ mã nguồn. Những ai muốn sử dụng zeromq trên Lua nhưng ngại học hỏi có thể tham khảo thư viện lua-zmq đã đề cập ở trên.

Một vài chia sẻ từ trải nghiệm cá nhân:

  • Việc biên dịch zeromq trên Windows bằng gcc khá phức tạp. Tôi đã nhờ một người bạn sử dụng Visual Studio để biên dịch và cung cấp file dll.
  • Thiết kế API của zeromq rất tinh tế với số lượng hàm hạn chế. Ví dụ, hàm zmq_init trả về kiểu void * đại diện cho context. Tôi đã cải tiến bằng cách định nghĩa kiểu rõ ràng:
1
2
typedef struct {} context;
typedef struct {} socket;

Lợi ích của cách này thể hiện rõ khi sử dụng ffi.metatype trong Lua để thêm phương thức cho các đối tượng. Cụ thể:

1
2
3
4
5
6
7
8
local context = {}
function init(io_threads)
  return newobj(libzmq.zmq_init(io_threads or 1))
end

function context:term()
  return check(libzmq.zmq_term(self))
end

Hàm newobj kiểm tra con trỏ NULL và trả về thông báo lỗi:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
local function strerror()
  return ffi.string(libzmq.zmq_strerror(libzmq.zmq_errno()))
end

local function newobj(obj)
  if ffi.cast("void *",obj) > nil then
    return obj
  else
    return false , strerror()
  end
end

Với các đối tượng msg, tôi giữ nguyên API gốc nhưng xây dựng thêm lớp bao bọc tiện dụng:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
function socket:send_string(str)
  local m = msg(str)
  local err = self:send(m)
  m:close()
  return err
end

function socket:recv_string()
  local m = msg()
  local err = self:recv(m)
  if err == nil then
    local ret = m:tostring()
    m:close()
    return ret
  else
    return false, err    
  end
end

Về hiệu năng, tôi cho rằng với các ứng dụng hạ tầng, nên sử dụng trực tiếp API gốc. Việc xây dựng lớp bao bọc nên tập trung ở tầng cao hơn. Đặc biệt với các hàm setsockoptgetsockopt, thay vì sao chép nguyên mẫu từ C, cần thiết kế lại cho phù hợp với đặc tính động của Lua.

Cuối cùng, lý do tôi chưa công bố mã nguồn là vì đang phát triển tích hợp sâu với Google Protobuf để tạo thành module hoàn chỉnh. Tương tự như thư viện protobuf cho Lua mà tôi từng phát triển nhưng chưa mở nguồn, đây vẫn là dự án đang trong quá trình hoàn thiện. Những ai thực sự quan tâm có thể liên hệ trực tiếp để nhận mã nguồn (dù thực tế việc xây dựng chỉ mất khoảng 2 giờ đồng hồ).

0%