Vấn Đề Độ Trễ Máy Chủ Trong Game Chiến Tranh Tam Quốc Phiên Bản Chiến Lược - nói dối e blog

Vấn Đề Độ Trễ Máy Chủ Trong Game Chiến Tranh Tam Quốc Phiên Bản Chiến Lược

Tác phẩm mới nhất của chúng tôi - Chiến Tranh Tam Quốc phiên bản chiến lược - đã ra mắt được một thời gian ngắn. Tuy nhiên, phản ứng từ thị trường rất tích cực khi thu hút được lượng lớn người chơi. Cùng với sự gia tăng nhanh chóng của người dùng, hệ thống máy chủ cũng gặp phải tình trạng độ trễ nghiêm trọng, đặc biệt là vào khung giờ cao điểm mỗi ngày.

Đội ngũ phát triển máy chủ của sản phẩm này ban đầu chưa từng tiếp xúc với nền tảng skynet, phải vừa học vừa làm trong thời gian rất ngắn để hoàn thành dự án. Điều này cho thấy tinh thần làm việc đáng khích lệ của nhóm. Cá nhân tôi không tham gia trực tiếp vào quá trình phát triển dự án, nhưng trong những ngày đầu tiên bộc lộ vấn đề thì vừa đúng dịp nghỉ lễ Quốc khánh nên tôi đang ở nước ngoài. Dù vậy, tôi vẫn tham gia từ xa vào các buổi thảo luận, hỗ trợ phân tích nguyên nhân. Sau kỳ nghỉ, tôi đã dành cả ngày hôm qua để họp và nắm rõ cơ chế vận hành của game, từ đó đề xuất một số phương án cải tiến khác nhau.

Hiện tại, vấn đề trực tiếp đang gặp phải là trong skynet tồn tại một dịch vụ khổng lồ quản lý toàn bộ dữ liệu cảnh quan game, chiếm khoảng 20GB. Tất cả các ô đất, quân đội, công trình đều nằm trong dịch vụ này. Đặc biệt, hệ thống đăng ký một lượng timer cực lớn để cập nhật các đối tượng. Kết quả là vào thời điểm game đông đúc, dịch vụ này tạo ra tới 500MB dữ liệu tạm thời mỗi phút.

Điều này gây áp lực cực lớn cho quá trình thu gom rác (GC), khiến dịch vụ bị độ trễ nghiêm trọng. Trong khi đó, các logic nghiệp vụ khác lại không tiêu hao nhiều CPU. Qua phân tích dữ liệu giám sát, tôi nhận thấy nguyên nhân chính nằm ở giai đoạn atomic operation của GC - giai đoạn “stop the world” không thể chia nhỏ. Nguyên nhân sâu xa là do dịch vụ này sử dụng quá nhiều bảng yếu (weak table). Khi số lượng mục trong bảng yếu lên tới hàng trăm nghìn, thời gian dọn dẹp và thiết lập lại các bảng yếu bị ảnh hưởng sẽ rất lâu.

Việc thiết kế hệ thống gần như gắn mọi đối tượng vào bảng yếu chỉ để theo dõi trạng thái tồn tại của các loại đối tượng trong bộ nhớ nhằm phát hiện rò rỉ bộ nhớ là một sự lạm dụng nghiêm trọng. Trong trường hợp thực sự cần thiết, việc quét toàn bộ VM cũng có thể thực hiện mục đích tương tự mà không làm gia tăng gánh nặng cho GC. Sau khi loại bỏ các bảng yếu không cần thiết này, tình hình đã được cải thiện rõ rệt.

Một phát hiện bất ngờ khác là trong giai đoạn quét (sweep) của GC, mỗi bước tiêu tốn thời gian gấp 10 lần so với giai đoạn đánh dấu (mark). Điều này khiến tôi băn khoăn vì về lý thuyết, giai đoạn quét chỉ cần duyệt qua danh sách liên kết hai chiều của các đối tượng GC. Với tỷ lệ khoảng 1/6 số đối tượng cần thu gom mỗi chu kỳ, thời gian chủ yếu tiêu tốn cho việc quét toàn bộ đối tượng hiện có. Giải thích cho hiện tượng này là do đơn vị tính toán của GC trong Lua: giai đoạn mark tính theo kích thước đối tượng, còn giai đoạn sweep lại dùng giá trị cố định (GCSWEEPCOST). Trên thực tế, chi phí đánh dấu một đối tượng và quét một đối tượng gần như tương đương, đặc biệt khi bộ nhớ cache gần như vô hiệu do dung lượng sử dụng lớn, giai đoạn sweep sẽ tiêu tốn lượng bộ nhớ gấp khoảng 10 lần so với giai đoạn mark.

Giải pháp cho vấn đề này, theo tôi, là cần điều chỉnh GCSWEEPCOST dựa trên thực tế hệ thống. Hiện tại dự án đang sử dụng chiến lược kích hoạt GC định kỳ thay vì để bộ cấp phát bộ nhớ điều khiển. Trong trường hợp có quy luật sử dụng bộ nhớ rõ ràng, việc điều chỉnh tham số mặc định sẽ hiệu quả hơn. Hiện tại máy chủ sử dụng 40GB bộ nhớ trong khi phần cứng hỗ trợ tới 128GB, rõ ràng là sự lãng phí. Tôi đề xuất tăng tham số GC pause để làm chậm chu kỳ GC đơn lẻ, giảm tổng chi phí GC dài hạn - vì càng kích hoạt GC thường xuyên, việc quét lại VM liên tục sẽ gây lãng phí.

Tuy nhiên, tôi cho rằng nguyên nhân gốc rễ nằm ở việc thiết kế kiến trúc máy chủ chưa hợp lý. Việc tạo ra một VM đơn lẻ chiếm hàng chục GB bộ nhớ là vấn đề cốt lõi (dù vẫn còn không gian tối ưu trong triển khai, nhưng điều này đòi hỏi kinh nghiệm sâu rộng với Lua mà không có giải pháp “nhanh chóng”). Trong kiến trúc skynet, chúng ta nên phân chia hợp lý các dịch vụ để tránh tình trạng một dịch vụ gánh vác quá tải.

Sau khi tìm hiểu kỹ cơ chế game, tôi nhận ra nguồn gốc vấn đề nằm ở thiết kế mơ hồ từ phía策划 (planner). Về bản chất, đây là một game theo lượt với hầu hết sự kiện diễn ra theo chu kỳ phút, khác biệt so với các game cùng thể loại trước đây khi cho phép quân đội di chuyển thực sự trên bản đồ ô vuông và xảy ra trận đánh bất ngờ trên đường hành quân. Điều này giống như một game RTS bị làm chậm lại, với hành động diễn ra theo đơn vị giây.

Nếu đơn giản hóa thành game theo lượt, quy tắc xử lý sự kiện đồng thời tại cùng một vị trí cần được xác định rõ ràng. Tuy nhiên hiện tại, thứ tự xử lý lại phụ thuộc vào logic tự nhiên của chương trình, dẫn đến việc sử dụng hàng loạt timer mà không đảm bảo thứ tự. Điều này gây khó khăn cho kiểm thử và phân chia nghiệp vụ.

So

0%