Một Số Cải Tiến Và Tiến Triển Của Skynet
Một số cải tiến đáng chú ý trong Skynet
Trong thời gian gần đây, toàn bộ tâm sức của tôi đều tập trung vào việc phát triển lại hệ thống Skynet - một dự án kế thừa thiết kế gốc bằng Erlang và hiện đang được viết lại hoàn toàn bằng C. Trong quá trình này, tôi gặp phải nhiều thách thức liên quan đến tính tương thích ngược với phiên bản cũ, đặc biệt là ở các định nghĩa giao diện. Điều này đòi hỏi phải duy trì sự tương thích để giúp các module ứng dụng phía trên có thể di chuyển dễ dàng mà không cần viết lại quá nhiều.
Hiện tại, công việc phát triển đã tiến triển đến giai đoạn xử lý các quy trình nghiệp vụ cụ thể. Tôi đã tiến hành di chuyển hàng loạt mã nguồn cũ sang kiến trúc mới. Để đảm bảo tính thuần khiết của phiên bản mã nguồn mở công khai, tôi duy trì hai nhánh phát triển song song trên máy cá nhân: một nhánh công khai trên kho lưu trữ GitHub, và một nhánh nội bộ cho dự án công ty. Quá trình đồng bộ này gặp phải một số rào cản kỹ thuật, đặc biệt khi tôi cố gắng ánh xạ một nhánh cục bộ lên nhánh master của kho lưu trữ nội bộ công ty. Có lẽ đây là hệ quả của việc tôi chưa nắm vững luồng làm việc Git chuẩn.
Hiện tại, nhánh master trên máy tôi luôn đồng bộ với master trên GitHub, nhưng phần lớn thời gian tôi làm việc trên nhánh “ejoy” - nhánh này lại được mapping với master của máy chủ nội bộ công ty. Để thực hiện push thuận lợi, tôi phải thiết lập upstream trong git config, nếu không mỗi lần push đều phải chỉ định rõ ràng. Khi cần sửa lỗi hoặc cập nhật tính năng nền tảng, tôi thường chuyển đổi giữa các nhánh: từ ejoy sang master để chỉnh sửa, sau đó quay lại ejoy và thực hiện rebase master. Tuy nhiên, tôi nhận ra rằng dùng merge có lẽ hợp lý hơn rebase trong trường hợp này, vì việc push sau rebase đòi hỏi phải dùng cờ -f (force).
Trong những thay đổi gần đây, điểm nổi bật nhất là việc thêm mới dịch vụ kết nối mạng bên ngoài (connection service). Dịch vụ này sau khi thiết lập kết nối sẽ có nhiệm vụ đẩy toàn bộ dữ liệu từ kết nối bên ngoài đến node đích. Thiết kế ban đầu của tôi có một số khiếm khuyết khi cố gắng áp dụng mô hình pull dữ liệu. Việc định nghĩa các giao diện pull dựa trên việc kéo từng dòng (dùng ký tự phân tách dòng) hoặc các khối dữ liệu cố định trong môi trường bất đồng bộ dễ dẫn đến tình trạng chồng chéo thao tác đọc, gây khó khăn trong việc phân tích luồng dữ liệu nhiều bước.
Tôi nhận ra rằng việc xử lý dữ liệu từ bên ngoài hệ thống Skynet (dù là kết nối đến từ gate hay connection khởi tạo) có bản chất khác biệt so với luồng dữ liệu nội bộ. Dữ liệu nội bộ có thể được tổ chức linh hoạt với con trỏ, trong khi dữ liệu ngoại vi phải là các khối liên tục với quy tắc phân tách gói rõ ràng. Để giải quyết vấn đề này, tôi thiết kế cơ chế nhận diện riêng biệt giữa dữ liệu nội bộ và ngoại vi bằng cách sử dụng session ID đặc biệt (0x7fffffff). Giá trị này đảm bảo không xung đột với các session ID do hệ thống tự sinh ra.
Để bảo đảm tính tương thích với giao thức truyền tin cũ và giao thức RPC, tôi phát triển thêm module lọc giao thức - có khả năng chuyển đổi định dạng cũ sang mới. Module này sẽ được gỡ bỏ sau khi hoàn tất quá trình di chuyển toàn bộ hệ thống. Đồng thời, nhằm tối ưu hiệu suất, tôi bổ sung giao diện chuyển tiếp gói tin (redirect packet) giúp giảm thiểu việc sao chép dữ liệu khi xây dựng các thành phần trung gian.
Một bước cải tiến quan trọng khác là việc viết lại hoàn toàn module truyền thông nội bộ harbor thay vì tiếp tục dùng thư viện zeromq như trước. Sau khi hoàn thiện module kết nối với cụm máy chủ legacy, tôi nhận ra việc dùng zeromq - một thư viện “nặng” - là không cần thiết khi chúng tôi chỉ sử dụng một phần nhỏ chức năng. Dù vậy, việc viết lại từ đầu hơn 1000 dòng mã mới không thực sự làm giảm lượng mã nguồn tổng thể (bản cũ cũng khoảng 1000 dòng), nhưng lại mang lại tính linh hoạt và hiệu quả cao hơn.
Hiện tại, hệ thống mới đã hoàn tất việc thay thế cụm dịch vụ xác thực độc lập cũ và có thể kết nối mượt mà với cụm game server legacy. Đây là bước đầu tiên trong kế hoạch thay thế hoàn toàn hạ tầng game server. Quá trình này cho phép tôi thực hiện các bài kiểm tra tải trọng thực tế.
Chúng tôi đang dùng giao thức ủy quyền dựa trên Kerberos, đòi hỏi sự phối hợp giữa nhiều dịch vụ và các thao tác truy vấn CSDL phức tạp. Bài kiểm tra ban đầu cho thấy hiệu suất tăng 3-4 lần so với framework cũ, dù kết quả này chưa thực sự mang tính quyết định. Có nhiều yếu tố ảnh hưởng đến hiệu suất phiên bản cũ, và ngay cả trong bài test đơn giản (echo server), mức cải thiện cũng không quá rõ rệt.
Tôi dự kiến sẽ thực hiện đánh giá chi tiết sau khi hoàn tất toàn bộ quá trình chuyển đổi. Lúc đó, tôi sẽ có thể giám sát chi li từng dòng mã C trong hệ thống mới. Trên phương diện hiệu suất, tôi chưa nhận thấy nhiều khoảng trống để tối ưu thêm ở tầng framework (đã được viết hoàn toàn bằng C). Tuy nhiên, qua các công cụ profiling, tôi nhận thấy phần lớn tài nguyên CPU (trên 80%) được tiêu tốn bởi máy ảo Lua trong các dịch vụ ứng dụng. Điều này cho thấy hướng cải tiến tiếp theo nên tập trung vào việc di chuyển từ Lua 5.2 sang Luajit2 để tăng tốc xử lý script.
Qua quá trình phát triển, tôi càng nhận ra tầm quan trọng của việc thiết kế kiến trúc cho khả năng bảo trì và mở rộng lâu dài. Mỗi cải tiến dù nhỏ đều góp phần xây dựng một hệ thống ổn định và hiệu quả hơn.