Nội dung bài viết
Video học lập trình mỗi ngày
Sau khi chúng tôi khám phá ra cách để một người không thể gian lận trong việc lấy hai phiếu giảm giá một lúc. Và có thể yên tâm trong việc quản lý phiếu giảm giá trong mùa dịch này, và có thể yên tâm. Nhưng mọi chuyện lại khiến chúng tôi trở nên hoang mang khi lượng người truy cập sẵn để đến giờ reload trang để hiện button "lấy phiếu giảm giá" rất cao. Và khi đến giờ G thì trang của chúng tôi không thể load. Sự việc khiến team của chúng tôi cảm thấy thất vọng, nhưng ngay sau đó My team bắt tay vào việc tối ưu hoá hệ thống này. Và sau 3 ngày chúng tôi đã test thành công với 1 triệu lượt truy cập tương tự. Vậy chúng tôi đã làm gì, và làm như thế nào, dùng công nghệ gì? Câu trả lời sẽ có trong ít phút nữa.
Bài viết liên quan
Nodejs - Thiết kế hệ thống phiếu giảm giá
3 khái niệm quan trọng khi xây dựng hệ thống backend
Sử dụng message queue với Rabbitmq
Tích hợp slack theo dõi bug hệ thống
Tình huống nào cần tối ưu hoá
Đương nhiên là tối ưu hoá thì luôn sẵn sàng cho mọi tình huống. Nhưng ở đây chúng tôi chỉ tập trung vào tính huống thực tế nhất đó là đặt vé tàu về quê ăn tết. Vé tàu tết 2022 là 30.000 vé. Nhưng khi mở bán thì lượng truy cập có khi đến vài triệu user tranh nhau để mua. Cuối cùng thất bại hết. Cả hệ thống tê liệt... Vậy chúng tôi đã thiết kế như thế nào?
Kiến trúc để tối ưu hoá hệ thống
Sau đây là một mô hình hệ thống không cao siêu như nhiều người nghĩ nhưng nó đã làm tốt và chi phí lại ít.
1) Về phía trình duyệt, lớp trên cùng sẽ thực thi một số mã JS
2) Lớp trang web, lớp này sẽ truy cập dữ liệu back-end và trang html sẽ được trả về trình duyệt
3) Lớp services, ngăn không cho lục lọi ở tầng dưới, cố gắng return data tại tầng này.
4) Lớp database, cuối cùng tệ lắm mới xuống đây, mysql là một điển hình.
Cách giải quyết tối ưu hoá
Chặn request càng xa và sớm ở tầng trên của hệ thống. Ai cũng hiểu lý do bị treo ở các mô hình truyền thống là do truy xuất ở backend quá nhiều, xung đột giữa đọc và ghi, tính đồng thời cao, gây ra phản hồi chậm, hầu hết các yêu cầu đều hết thời gian chờ. Do đó đặt hàng thành công tỷ lệ rất nhỏ. Bạn biết đấy thực tế có 30.000 vé mà có đến 1 triệu người đăng ký mua, về cơ bản là không thành công. Tỷ lệ yêu cầu hiệu quả là 0
Tận dụng triệt để cache Đây là một kịch bản ứng dụng điển hình với việc đọc nhiều hơn đọc ít hơn [Thực tế, chỉ có 30.000 vé cho một chuyến tàu, 1 triệu người sẽ đến và mua nó và maximum có 30.000 người đã đặt hàng thành công . Những người khác đang kiểm tra hàng tồn kho, và tỷ lệ ghi chỉ là 0,1%, tỷ lệ đọc là 99,9%], rất thích hợp để lưu vào bộ nhớ đệm.
Các bạn nhớ đọc lại những bài viết liên quan để hiểu được câu chuyện thực tế trên, đọc hiểu nhiều, mất mát gì mà không đọc?
Giải quyết chi tiết.
Xử lý ngay trên trình duyệt
Ở bài viết về Cách kiểm soát đồng thời trong javascript với 1 triệu url tôi có nhắc đến hành vi nhấp liên tục vào button nhận phiếu giảm giá. Thì ở đây tôi cũng vậy, việc người dùng có thói quan vô thức cứ click hoài vào một button. Giống như bấm nút ở thang máy vậy. Nó có gì hữu ích không? Chính vì vậy, các bạn làm nhớ chặn ngay trên browser, khi user click vào mua vé thì nên chuyển trạng thái màu xám, or hiện loading, vừa chặn user click nhiều lần vừa đẹp và chuyên nghiệp nữa. Ngoài ra ở cấp độ js thì nên hạn chế buộc người dùng có thể chỉ gửi một request trong vòng x giây mà thôi. Làm thế nào để thục hiện điều đó thì tôi có hướng dẫn ở bài viết về debounce vs throttle. Các bạn có thể đọc thêm ở đó.
Xử lý ở proxy web
Xong tầng trình duyệt thì bắt đầu xuống tầng proxy rồi. Các bạn cũng biết rằng, ngăn chặn ở trình duyệt thôi là chưa đủ vì đa số các lập trình viên cao cấp họ thường lấy http của bạn chạy vòng for không cần thông qua trình duyệt làm gì? Chính vì vậy bạn phải làm thêm tầng này nữa. Làm thế nào để ngăn chặn?
Chúng tôi thường có quy ước giống nhau đó là thêm vào UUID trong mỗi session, nhằm giới hạn tần suát truy cập, sử dụng cache nếu có tại tầng này. Và quan trọng là tất cả đều phải return trong vòng x giây. Đảm bảo lưu lượng thông suốt. Nên nhớ hãy tính toán một UUID cho phép request bao nhiêu x giây. Với sự hạn chế lưu lượng như vậy, 99% lưu lượng truy cập sẽ bị chặn ở tầng này.
Tầng services
Theo thiết kế của chúng tôi thì tầng services này bao gồm chủ yếu là cache và message queue. Chúng tôi biết, chỉ có 30K vé tàu thôi cho nên gửi 1 triệu request tới để làm gì, vô nghĩa. Chính vì vậy chúng tôi thực hiện một task queue. Mỗi lần chỉ có một số lượng hạn chế yêu cầu ghi chuyển đến lớp dữ liệu, nếu không thành công vì hết vé thì tất cả trong queue còn lại sẽ trả lại status là 'hết vé'. Đó là đăng ký mua hay gọi là ghi. CÒn đọc thì dễ dàng hơn vì chúng tôi có cache, chống lại 10K request mỗi giây. Điều đó với cache thì đơn giản.
Tầng database
Nếu mà xuống được đây thì có lẽ xong phim, trừ khi bạn cấu hình nhiều server database và thiết kế table con như bài viết trước thì may chăng. Bạn nên nhớ ở trường hợp như trên mà để nhiều server database làm việc thì quả là phí phạm. Xử lý tốt 3 tầng trên thì tầng này tương đối nhẹ nhàng.
Tóm tắt
Nó đã quá rõ để tóm tắt một lần nữa. Ở đây tôi chỉ muốn nhắn gửi tới anh em đồng nghiệp, bất kể ai làm ngôn ngữ nào, làm ở vị trí nào cũng có trách nhiệm tối ưu kiến trúc tầng của mình. Việc tối úu không chỉ riêng mỗi người làm backend. Đừng có suy nghĩ đó, bỏ đi. Bạn nào chưa hiểu cố truyện có thể đọc lại những bài viết liên quan mà tôi đã liệt kê.
Xin chào và happy coding!
Bài viết có sử dụng hình ảnh và tham khảo trên internet. Nếu có bản quyền vui lòng liên hệ: anonystick@gmail.com. Cảm ơn nhiều!