Chat Service Architecture: Servers

Bài tóm tắt Riot Games 2015 3. Quy mô lớn Độ khó: Nâng cao

Riot Games · 2015 · 3. Quy mô lớn · Độ khó: Nâng cao — bài gốc

Phần 2/3 của series chat League of Legends. Phần 1 (giao thức XMPP) wiki bỏ qua vì chủ yếu về giao thức phía client. Phần 3 (lưu trữ): Riot Games 2015.

Bối cảnh

  • Chat LoL (2015): mỗi ngày thường định tuyến khoảng 1 tỷ event (presence, message, IQ) và hàng triệu REST query; một ngày tháng 7 có 1,7 triệu lượt kết bạn mới.
  • Triển khai theo region ("shard"): mỗi region một cụm chat riêng, không nói chuyện được giữa các shard.

Kiến trúc

  • Cụm 6–12 máy mỗi region, phần cứng khác nhau theo tuổi; mọi node tự trị và thay thế được, giữ headroom rộng để tắt một nửa cụm khi nâng cấp mà không gián đoạn.
  • Erlang 90%, C 10%: profile kỹ bằng cprof, fprof, percept, lcnt rồi mới viết lại phần xử lý chuỗi/XML nặng nhất bằng C và biên dịch một phần thư viện chuẩn bằng HiPE: CPU giảm hơn 60%, bộ nhớ mỗi session từ hơn 150 kB còn 25 kB. Lý do: term của Erlang bất biến nên thao tác chuỗi nhiều luôn đắt.
  • Cụm Erlang kết nối đầy đủ (mỗi VM một kết nối TCP tới mọi VM khác). Node chia sẻ ít dữ liệu nhất có thể: chỉ vài bảng KV trong Mnesia, nhân bản đầy đủ trong RAM, ánh xạ JID → process xử lý session. Đọc luôn cục bộ nên định tuyến nhanh; đổi lại mọi cập nhật phải nhân bản tới mọi node. Load test cho thấy lên tới ít nhất 30 server chưa suy giảm.
  • Tự lành sau netsplit: mỗi node nghe sự kiện thay đổi topology của VM. Node khác rơi khỏi cụm thì xoá session của node đó khỏi bảng cục bộ; khi nó quay lại, hai bên đẩy và tải trạng thái của nhau. Mỗi node là nguồn chính thức cho dữ liệu của chính nó. Client cũng tự kết nối lại khi server của mình bị tắt.
  • Luồng một tin nhắn: mỗi player một process c2s (giữ socket, XML parser, danh sách bạn, rate limiter). Tin nhắn được kiểm tra rate limit, chống giả mạo, danh sách chặn; tra bảng định tuyến; người nhận offline thì lưu vào kho bền (MySQL ở shard cũ, Riak ở shard mới), online thì gửi bằng message passing của Erlang.
  • REST nội bộ (Cowboy, Swagger) phục vụ social graph cho các service khác (tặng quà, gợi ý đội, xếp league). Muốn chuyển từ load balancer tập trung sang service discovery + client-side load balancing.

Kết quả

Theo Riot, năm 2015 đạt năm số 9 uptime ở mọi region dù phần cứng thỉnh thoảng hỏng.

Bài học cho SWE

  • Profile trước rồi mới viết lại phần nóng bằng ngôn ngữ cấp thấp hơn; chỉ 10% code là đủ (Realtime và fan-out).
  • Bảng định tuyến nhân bản đầy đủ cho đọc cục bộ nhanh nhưng giới hạn khả năng scale ngang; biết trần của nó (ở đây khoảng 30 node).
  • Thiết kế cho netsplit: mỗi node chịu trách nhiệm dữ liệu của mình và tự đồng bộ lại khi nối lại, không cần người can thiệp (Độ tin cậy và multi-region).

Liên quan