Trong bài viết là 08 cách mình đã dùng để scale website có lượng truy cập lớn, nó đã giải quyết nhiều vấn đề thực tế mà mình đã gặp phải. Hy vọng nó cũng có thể giúp bạn bằng một trong những cách được đề cập dưới đây.
Các thành phần cần tối ưu:
- Deploy trên K8s
- Front end
- Load balancer phần Server
- Database replication
- Cache layer
- Tối ưu logging
- Dùng Message Queue nếu có nhiều tác vụ khác
Deploy trên K8s
- Mình nghĩ để vận hành 1 website có lưu lượng truy cập lớn thì xung quanh website bạn có nhiều services khác, nên giải pháp của mình là dựng luôn cluster K8s để dùng chung (bạn có thể dùng Docker Swarm).
- Hồi trước mình deploy service trực tiếp trên con VM, mỗi lần cục monitoring báo qua Slack là service vừa ngủm thì mình phải dùng script để Start nó lại, nó ngủm lúc nữa đêm cũng phải thức để làm. Bây giờ dùng K8s thì khoẻ hơn, khi nó ngủm thì có cơ chế tự Restart.
- Phần này nói đầy đủ sẽ rất dài, mình nói ngắn gọn những điểm mình thích nhất ở K8s là:
– Scaling/Auto Scale website
– Scrolling update
– Điều phối các container khá hiệu quả
– Quản lý work load, log khá tiện
– Có cơ chế load balancing
Tối ưu front end
Về CDN:
- CDN là nó là một hệ thống máy chủ trên toàn cầu (số lượng tùy theo mỗi nhà cung cấp dịch vụ) làm nhiệm vụ lưu bản sao của các nội dung tĩnh (file javascript, css, hình ảnh, video,…) bên trong website, sau đó phân tán nó ra nhiều máy chủ khác và nó sẽ gửi tới cho người dùng khi họ truy cập vào, điều này có thể giúp bạn có thể scale website tốt hơn thay vì lưu giữ các nội dung tĩnh trên server hệ thống của mình.
- Ưu điểm khi dùng CDN:
+ Tiết kiệm băng thông cho máy chủ của bạn -> tiết kiệm chi phí
+ Tăng tốc lược truy cập
+ Tiết kiệm dung lượng lưu trữ - Nhược điểm khi dùng CDN:
+ Có nguy cơ bị downtime nếu dịch vụ có vấn đề
+ Có thể ảnh hưởng đến SEO
+ Giá cũng không rẻ - Một số lưu ý khi sử dụng CDN:
+ Tuỳ nhà cung cấp, bạn sẽ trả mức phí cho nhà cung cấp (có thể xem xét dùng vài loại CDN miễn phí nhưng mình không khuyến khích dùng vì website bạn có lượt truy cập lớn, dùng những dịch vụ miễn phí thì hơi rủi ro)
+ Nếu dùng CDN mà nó không có ở Việt Nam thì có thể làm website bạn chậm hơn
+ Bạn cần có 1 CDN dự phòng hoặc cơ chế dự phòng load từ origin nếu sự cố CDN xảy ra, mình thấy lâu lâu CDN có sự cố ngắt tạm thời.
Tối ưu file css
- Minify css
- Đặt css trong thẻ header (dùng cơ chế để làm tự động việc này khi build project) -> Khi load HTML xong, không cần phải tải file css mà render luôn.
- Hạn chế dùng @import
- Nếu nói kỹ vấn đề này thì khá dài, bạn tìm hiểu thêm trên Google nhé!
Tối ưu file js
- Minify js
- Nếu file js đó không cần thiết đặt ở đầu trang để xử lý 1 số tác vụ đặc biệt thì nên để ở cuối trang.
- Tuỳ trường hợp xử lý mà quyết định đặt file có đặt tag defer cho file js đó không
- Đặt những file js ở CDN
- 1 số script tracking trên site của bên thứ 3 có thể làm giảm performance của website. Trước đây công ty mình có làm việc với 1 partner để tracking hành vi người dùng trên website TMĐT: track user view_product, add_to_cart, remove_from_cart, … nhằm tối ưu trải nghiệm người dùng và phục vụ 1 số phân tích khác cho team Marketing. Script họ làm site mình chậm đi, lâu lâu Server của họ bị down… Nên khi làm việc với các đối tác bên ngoài các bạn cần ký SLA ràng buộc về timeout, những tác vụ đặc biệt cần set timeout khi gọi lên server, hoặc có những tính năng liên quan script đó nên để trong khối try-catch …
Caching
- Ở header có option cache-control, bạn có thể cache để browser không phải tải lại hình ảnh, file js,…
- Tuy nhiên việc caching này cần thận trọng, đối với những nội dung thường xuyên thay đổi, nếu cache quá lâu thì nó sẽ còn giữ nội dung cũ đến khi hết hạn, nếu đặt cache quá ngắn thì load nhiều lần sẽ gây chậm.
Hạn chế dùng thư viện
- Ưu điểm dùng thư viện là code nhanh
- Nhược điểm là có nhiều thành phần không dùng đến làm browser tải nhiều hơn gây chậm
Các tối ưu khác
- Đối với hình ảnh có thể dùng cơ chế lazy load.
- Hình ảnh thì nên resize cho phù hợp với website, nhất là ở trang web có nhiều hình ảnh như TMĐT.
- Hạn chế load ảnh dạng base64, mình thấy nhiều website dùng cách này ở CSS làm việc load CSS hơi vất vả.
- Lúc trước mình làm ở 1 site TMĐT, khi có 1 chương trình khuyến mãi là lượng truy cập có thể cao gấp 20 lần bình thường, khi đó chỉ load trang home thôi cũng lâu (tầm 5s), do trang home lúc khuyến mãi không cá nhân hoá nội dung theo người dùng nên mình quyết định dùng cơ chế lưu toàn bộ HTML vào Cache (Redis), khi người dùng vào trang home thì chỉ lấy từ cache ra, nên khá nhanh.
Load balancer phần Server
- Load balancer phần này mình nghĩ khá quan trọng khi muốn scale website có lượt truy cập cao, ở phần này mình sẽ không đi sâu vào các thuật toán mà sẽ giải thích về cách hoạt động ở mức high level.
- Load balancer phân phối hiệu quả lưu lượng truy cập đến trên một nhóm servers.
Mô tả
- Ở hình 3, người dùng sẽ không tương tác trực tiếp với Web Server, mà tương tác thông qua bộ Load Balancing. Các Web Server sẽ liên lạc với nhau thông qua Private IP (còn gọi là Internal IP), các Private IP này không truy cập được từ môi trường internet bên ngoài. Bằng cách sử dụng Load Balancing, những yêu cầu từ người dùng sẽ được tiếp nhận và xử lý trước khi phân chia đến các máy chủ. Đồng thời, quá trình phản hồi cũng được thông qua Load Balancing, ngăn cản việc người dùng giao tiếp trực tiếp với máy chủ, ẩn đi thông tin và cấu trúc mạng nội bộ, từ đó chặn đứng những cuộc tấn công mạng hay truy cập trái phép…
- Load balancer sẽ kiểm tra Web Server trước khi phân bổ tài nguyên. Nếu Web Server 1 bị ngủm, thì tất cả traffic truy cập sẽ được chuyển đến Web Server 2. Giả sử ta dùng mô hình như Hình 1 nếu Web Server bị ngủm thì cả hệ thống sẽ không truy cập được, dẫn đến trình trạng user load trang không được -> User sẽ chửiii
- Nếu lưu lượng truy cập tăng quá cao mà cả 2 Web Server không đủ để xử lý, khi ấy ta có thể dễ dàng thêm 1 Web Server mới nữa để đáp ứng nhu cầu truy cập.
-> Theo mô hình trên, ta thấy việc scale Web Server có thể đáp ứng được nhu cầu truy cập, tuy nhiên nhìn về phần Database, nếu lượng truy cập quá cao thì khả năng không đáp ứng được, nên các bạn tham khảo phần tiếp theo là Replication cho Database nhé.
Database replication
Cơ chế master-slave
- Ở Hình 4, khi có 1 request, Load Balancing nó sẽ được chia tải và chọn 1 trong 2 Web Server để nhận request, sau đó muốn đọc dữ liệu từ Database thì nó sẽ chọn 1 trong 2 Slave Database để đọc dữ liệu, hệ thống sẽ chia tải.
- Master Database hỗ trợ việc insert/update/delete dữ liệu từ Web Server. Sau đó các Slave Databases sẽ nhận được copy từ Master Database, và các Slave Databases này hỗ trợ việc đọc dữ liệu từ Web Server.
- Khi thực hiện Database Replication thì ta sẽ phục vụ được nhiều truy vấn cùng một lúc hơn. Ví dụ ta có một máy chủ có thể phục vụ được 1000 truy vấn cùng một lúc. Nếu ta dựng thêm 4 Slaves nữa là thành 5 Slaves có cấu hình tương tự và đặt một hệ thống cân bằng tải ở giữa thì số lượng truy vấn mà hệ thống có thể phục vụ được đồng thời là 5×1000 truy vấn (tính tương đối, ngoài ra còn các yếu tố khác ảnh hưởng).
- Khi 1 Slave Database bị ngủm, thì Web Server có thể đọc dữ liệu từ 1 trong các con Slave còn lại.
- Nếu con Master Database bị ngủm, thì 1 trong những Slave Database sẽ được bầu làm Master Database.
Tối ưu bằng thiết kế database
Ngoài ra cần tối ưu phần thiết kế database, ví dụ như:
- Đối với dữ liệu cần tính toán thì ta cần có các cơ chế chuẩn bị dữ liệu để truy vấn (aggregation sẵn). – Ví dụ: đối với data report cho hệ thống bán hàng theo ngày, khi user mua hàng thành công đều được ghi vào table sale.transaction. Mỗi khi user vào xem report revenue theo ngày thì ta không nên sum trực tiếp từ bảng sale.transaction mà ta nên tạo schedule mỗi giờ chạy aggregation sau đó lưu kết quả vào 1 table tên sale.daily_transaction chẳng hạn, khi cần report thì ta query vào table sale.daily_transaction và union với data mới nhất của table sale.transaction (data chưa được merge vào sale.daily_transaction), làm vậy phía database sẽ nhẹ nhàng hơn.
- Ở 1 số trường hợp nên Sharding data để query được performance tốt hơn
- Data ở nhiều node khác nhau nếu dùng lệnh JOIN có thể làm performance tệ đi, nên xem xét de-normalization dữ liệu.
-> Lưu ý: Việc Replication này giúp hệ thống chịu tải được số lượng lớn users truy cập vào hệ thống chứ không làm tăng tốc truy vấn dữ liệu nhé anh em. Nếu muốn làm tăng tốc truy vấn dữ liệu thì anh em tham khảo phần tiếp theo là Cache Layer.
Cache layer
- Cache trong trường hợp này được hiểu nó như bộ nhớ tạm được lưu trên RAM nên nó truy vấn nhanh hơn nhiều so với truy cập vào database trên disk. Tuỳ theo nhu cầu sử dụng, mình thường dùng Redis để caching. Mình dùng Redis ngoài việc Caching thì mình còn dùng tính năng Pub/Sub của nó như 1 Message Queue.
- Hình 6 mô tả Cache layer ở mức high level, ở đây Caching bạn nên dùng dạng Master-Slaves để chia tải cũng như khi nó down thì hệ thống vẫn hoạt động ổn. Mình mô tả ở dạng high-level để các bạn dễ tưởng tượng thôi.
- Một số lưu ý khi dùng với Cache:
+ Không nên dùng nó thay thế cho Database, vì nó không đầy đủ tính năng của 1 Database, dùng nó như bộ đệm để tăng performance.
+ Tính toàn vẹn dữ liệu (consistency) cần được đảm bảo, nên cần có cơ chế cho vụ này.
+ Cần backup dữ liệu trên Cache
+ Cần có worker offline xử lý nếu Cluster của Cache có vấn đề xảy ra. Ví dụ mất dữ liệu thì worker đó chạy để insert data vào Cache hoặc lấy từ dữ liệu backup.
+ Khi Cluster Cache bị down, thì hệ thống vẫn dùng bình thường do vẫn dùng Database, chỉ là performance sẽ không được như mong muốn.
Dùng Message Queue nếu xử lý thêm tác vụ nặng khác
- Thường khi phát triển 1 hệ thống mình luôn hướng đến Microservices. Thường mình dùng Message Queue để giao tiếp giữa các worker với nhau. Tuy nhiên nếu bạn không đang dùng Microservices thì ở những tác vụ nặng bạn không nên dồn hết cho Web Server xử lý mà nên dựng các con Workers riêng, sau đó dùng Message Queue làm cầu nối cho/nhận dữ liệu.
- Mình cũng thường dùng nó cho các Worker send email, Worker push notification đến mobile App,… Đối với những tác vụ nặng có khả năng ảnh hưởng đến Web Server chính thường mình sẽ tách worker riêng. Để khi nó xử lý quá nặng và không may bị down thì những tính năng còn lại của Web Server không ảnh hưởng. Ví dụ trang TMĐT, Worker search bị down thì người dùng vẫn dùng được, vẫn checkout giỏ hàng được.
Kể chuyện
Hồi trước mình có nhận project của 1 công ty ở Canada dạng freelancer. Hệ thống web đó đã chạy ổn định gần 10 năm, họ muốn phát triển 1 Search Engine để tìm kiếm tài liệu ở dạng PDF. Dữ liệu gần 100,000 file PDF, trung bình mỗi file gần 100 trang, có file lên đến gần 1,000 trang. Khi user tìm kiếm theo nội dung và kết quả trả về: file đó, nội dung đó nằm ở trang nào.
Với bài toán ấy mình nghĩ không thể nào viết thêm function trong phần Web Server vì code đã 10 năm và đang chạy ổn. Cộng với việc latency của Search Engine này khá cao, IO của hệ thống lên cao có thể ảnh hưởng đến cả Web Server hiện tại. Lúc này mình tạo mới 1 worker và dùng Message Queue làm cầu nối, sau 1 thời gian căng não thì cũng xong, hiện tại hệ thống hoạt động khá tốt.
Tối ưu logging
- Theo mình thì logging khá quan trọng, dùng để track nguyên nhân gây lỗi. Đối với những hệ thống như adtech, TMĐT… Mỗi giây mà hệ thống bị down thì công ty mất tiền giây đó, nếu không có log khi có lỗi xảy ra, cả team ngồi debug sẽ rất lâu. Khi ấy trừ lương cả team cũng không đủ bù vào phần tiền bị mất. hihi. Khi website bạn có lượt truy cập nhỏ, thì việc ghi log không quá quan trọng, nhưng khi website có hàng triệu lượt truy cập mỗi ngày thì câu chuyện nó khác lắm.
- Ở 1 số thư viện có tuỳ chọn ghi log, nên chú ý nếu nó ghi trực tiếp vào file, database trên ổ cứng đang chạy Web Service có thể làm giảm hiệu năng của Web Service. Theo mình nên chọn 1 CSDL timeseries chuyên ghi log, và lưu log tập trung ở service này, mình thường dùng Elasticsearch để lưu log (dùng theo hệ ELK).
- Nên chọn loại log cần lưu lại, không nên ghi toàn bộ các loại log: DEBUG, INFO, WARNING, ERROR. Cũng tuỳ theo từng nhu cầu của từng service.
- Cách đây khá lâu mình xử lý dữ liệu realtime cho 1 công ty (mỗi giờ xử lý tầm 10 triệu events), lúc đó chưa có nhiều kinh nghiệm. Lúc coding thì lưu toàn bộ log vào, nghĩ nó bình thường và đẩy toàn bộ lên production. Đến khi lên production thì performance rất tệ, ngồi debug với anh Senior thì anh bảo bỏ DEBUG và INFO thử xem, và performance nó tốt hơn hẳn.
Tối ưu phần cứng
- Có 1 câu chuyện vui là lúc trước mình nhận dự án freelancer cho 1 công ty ở Đức. Công ty này đã dùng hệ thống được 2 năm nhưng bây giờ nó chậm lắm. Hệ thống chỉ dùng cho nội bộ của công ty, họ nhờ mình hỗ trợ. Sau khi xem code và cơ chế làm của anh Ấn Độ, mình thấy anh ta đã làm rất chuẩn, nhiều cách xử lý rất hay. Tầm 5 ngày xem mô hình và review code thì mình thấy mình không đủ khả năng để optimize, vì không có gì để cho mình optimize cả. Xong xem lại phần cứng thì thấy chạy trên con VM với ổ cứng HDD. Mình đã đề xuất nâng cấp SSD, hỗ trợ họ migrate. Sau khi migrate xong thì nó chạy ngon hơn hẳn. Đã gần 3 năm rồi nó vẫn chưa chậm. hihi.
- Mình nghĩ nếu có kinh phí anh em nên đưa hết lên Cloud (Google, AWS,…). Ở đó khi muốn upgrade phần cứng thì khá dễ dàng, cộng thêm có đội hỗ trợ mình.
******************************
Trên đây là những cách mình đã từng làm giúp scale website và khiến hệ thống nhanh hơn. Mình viết ở mức high-level thôi, còn chi tiết từng phần bên trong phải làm rất nhiều thứ nữa.
Với kiến thức hạn hẹp, mình mong được chỉ giáo từ anh em, anh em có cách nào hay thì bình luận vào để cùng nhau học hỏi nhé.