Hai cách để mang thêm tải
Chào mừng
Khi một dịch vụ bắt đầu nứt dưới tải, một nhà điều hành phải đối mặt với sự lựa chọn. Tạo ra một máy tính lớn hơn (có nhiều CPU, nhiều RAM, đĩa nhanh hơn). Hoặc thêm nhiều máy tính mỗi cái làm công việc giống nhau.
Con đường đầu tiên được gọi là tích hợp dọc (tăng lên). Con đường thứ hai được gọi là tích hợp không (tăng ra).
Bài học này dạy lý do tại sao hầu hết mọi kiến trúc web hiện đại chọn không stateful và điều gì làm cho lựa chọn đó khả thi. Câu trả lời giấu trong một từ: state.
Sau khi học xong bạn sẽ hiểu:
- Đường cong chi phí của tích hợp dọc so với tích hợp không và nơi nào mỗi cái hợp lý
- Ý nghĩa của 'stateful' & 'stateless' trong thực tế, và tại sao một trong số chúng nhân lên rẻ
- Toán học để cỡ một hạm đội dưới tải dự kiến và sự xâm chiếm
- Quy tắc không gian đầu để tránh một tầng sụp đổ qua gối đầu
- Đâu là vị trí của state (nó không biến mất) và cách đẩy nó ra khỏi các lớp cần phải tăng
Tại sao không stateful chiến thắng sau một ngưỡng
Tích hợp dọc: Một hộp lớn hơn
Ưu điểm: đơn giản. Không cần thay đổi mã. Không cần phối hợp. Quá trình giống như bây giờ có nhiều CPU.
Nhược điểm: mái vòm. Máy ảo lớn nhất có hạn chế RAM & core thương mại. Trên đó, không có tiền mua thêm không gian đầu. Chi phí tăng siêu tuyến tính sau ngưỡng đẹp nhất của một nhà cung cấp. Một sự cố của máy tính này làm sụp đổ toàn bộ dịch vụ.
Tích hợp không: Nhiều hộp nhỏ hơn
Ưu điểm: không mái vòm (tối đa lên đến sự sẵn lòng thanh toán cho & phối hợp máy tính). Dung lượng tăng tuyến tính với số lượng bản sao, dự đoán được. Một sự cố của một bản sao loại bỏ 1/N dung lượng, không phải 100%.
Nhược điểm: đòi hỏi workload phải hỗ trợ. Một số workload (cơ sở dữ liệu lớn, một máy chủ stateful giữ các phiên trò chơi đang hoạt động) chống lại tích hợp không. Phối hợp & phân phối tải trở thành vấn đề vận hành.
Crossover: bất kỳ dịch vụ sản xuất nào cần tồn tại sau sự cố của một máy tính phải chạy trên ít nhất hai máy. Khi bạn chấp nhận hai, bạn đã chọn tăng ngang. Từ đó, câu hỏi không phải 'chúng ta nên không?' mà là 'chúng ta có thể thêm replica tiếp theo với chi phí thấp nhất có thể?'
Khóa khả năng: một workload không giữ bất kỳ trạng thái yêu cầu nào trên máy tính itself. Sau đó, bất kỳ replica nào cũng có thể trả lời bất kỳ yêu cầu nào, & thêm một replica tăng khả năng với không cần phối hợp.
Stateful vs Stateless trong Thực tiễn
Trạng thái Không Biến mất, Nó Chỉ Di chuyển
Component Stateful: giữ thông tin mà mất đi sẽ thay đổi hành vi. Một cơ sở dữ liệu giữ tài khoản người dùng. Một cache giữ token phiên. Một công việc giữ kết nối luồng streaming lâu dài cho một người dùng cụ thể.
Component Stateless: không giữ thông tin mà mất đi sẽ quan trọng. Một tầng web đọc một yêu cầu, truy vấn một cơ sở dữ liệu, & viết một phản hồi. Mỗi yêu cầu độc lập; tầng nhớ không điều gì giữa các yêu cầu.
Ý tưởng chính: trạng thái không biến mất khỏi hệ thống. Nó di chuyển đến một lớp được thiết kế để giữ nó (một cơ sở dữ liệu, một cluster Redis, một bộ lưu trữ đối tượng). Các lớp đối diện với giao thông có thể trở nên stateless, & các lớp stateless có thể tăng ngang vì bất kỳ replica nào cũng có thể trả lời bất kỳ yêu cầu nào.
Test Thực tế: nếu bạn giết ngẫu nhiên một quá trình trong tầng này & khởi động lại nó, liệu bất kỳ người dùng nào sẽ trải nghiệm một câu trả lời sai hoặc một phiên bị mất không? Nếu có, nó giữ trạng thái. Nếu không, nó không giữ trạng thái.
Ví dụ
- Một quá trình web Python đọc yêu cầu, truy vấn Postgres, trả về JSON: stateless. Trạng thái sống trong Postgres.
- Một quá trình web Python giữ giỏ hàng người dùng trong bộ nhớ local: stateful. Giết quá trình mất giỏ hàng.
- Một server WebSocket giữ kết nối mở đến người dùng chat: stateful trong ý nghĩa kết nối. Giết quá trình làm mất kết nối; khách hàng phải reconnect. Thường thì những thứ này vẫn có thể tăng ngang với sự cẩn thận (kết nối dính, consistent hashing).
- Một cache Redis che giấu Postgres: có trạng thái cho nội dung cache, nhưng chấp nhận được nếu misses cache là tolerable. Một sự cố replica có nghĩa là miss cache, không phải là mất dữ liệu.
Lập trình cho khả năng mở rộng ngang hàng = đẩy trạng thái ra khỏi lớp cần mở rộng.
Kiểm toán một Tier nghi ngờ
Một đội ngũ phát triển chạy một API khuyến nghị trên 6 VM backend phía sau một reverse proxy. Ứng dụng: đọc ID người dùng từ yêu cầu, truy xuất hoạt động gần đây của người dùng từ Postgres, chạy một thuật toán đánh giá, trả về một danh sách các sản phẩm khuyến nghị. Hai hành vi không tiêu chuẩn:
- Ứng dụng giữ một 'cache hoạt động người dùng gần đây' trong bộ nhớ quá trình, được populate trên yêu cầu đầu tiên cho một người dùng, tái sử dụng trên các yêu cầu tiếp theo.
- Ứng dụng sử dụng các phiên làm việc dính: một khi một người dùng truy cập VM #3, tất cả các yêu cầu tiếp theo của họ sẽ được gửi đến VM #3 (proxy được cấu hình cho routing dính trên một cookie).
Công thức Replica
Công thức Dung lượng đơn giản
Khi một tier trở nên stateless, việc định cỡ nó trở thành một phép toán. Bạn cần đủ replica để dòng tải ổn định đến và đi với cùng một tỷ lệ, với một khoản dự phòng cho sự tăng vọt.
Công thức:
replicas = ⌈ (peak_load × surge_factor) / per_replica_capacity ⌉ + headroom
Trong đó:
- peak_load: lượng yêu cầu tối đa sustained requests/second bạn mong đợi trong hoạt động bình thường
- surge_factor: một nhân số để bao gồm các đợt tăng vọt trên peak (thường là 1,5x đến 2x cho lưu lượng truy cập có thể dự đoán, 3x hoặc nhiều hơn cho viral / không thể dự đoán)
- per_replica_capacity: lượng yêu cầu/second một replica xử lý với độ trễ và sử dụng chấp nhận được (thường được đo ở 70% CPU, không ở giới hạn tối đa)
- headroom: các replica thêm để một số sự cố replica không làm sụp đổ tier (thường là 1-2 replica cho các đội tàu nhỏ, 10-20% cho các đội tàu lớn)
Ví dụ thực tế: một backend chịu tải 100 req/s với 70% CPU trên mỗi bản sao. Tải trọng đỉnh là 600 req/s. Bạn mong đợi những cơn sóng 2 lần. Bạn muốn dịch vụ sống sót được 2 sự cố bản sao.
bản_sao = ⌈ (600 × 2) / 100 ⌉ + 2 = 12 + 2 = 14 bản sao
Quy tắc 80%
Độ khả năng của mỗi bản sao không là điểm bão hòa. Đo khả năng ở 70-80% CPU, không phải 100%.
Khoảng từ 80% sử dụng, đường cong hàng đợi tăng cao: một hàng đợi chạy trong 10 ms ở 60% sử dụng sẽ mất 80 ms ở 90% sử dụng. Latency, không phải throughput, là thứ phá vỡ trước. (Lektion liên kết geometry_of_stateless_horizontal_scaling deriving this curve mathematically.)
Autoscaling vs Static Provisioning
Static: cung cấp cho đỉnh × không gian đầu của sóng và chấp nhận chi phí của việc chạy ở mức thấp sử dụng ngoài giờ cao điểm.
Autoscaling: một controller thêm & loại bỏ bản sao dựa trên sự sử dụng được quan sát, target latency, hoặc độ sâu hàng đợi.
Lưu ý Autoscaling: thời gian khởi động lạnh quan trọng. Nếu một bản sao mới cần 2 phút để khởi động, autoscaling không thể đáp ứng một cơn sóng 30 giây. Autoscaling trưởng thành giữ một bể ấm của bản sao đã chuẩn bị sẵn chỉ dưới ngưỡng tăng lên.
Kích cỡ một hạm đội cho một dịch vụ mới
Đội của bạn lên kế hoạch phát hành một API metadata video. Các tiêu chuẩn cho thấy một bản sao xử lý 250 req/s với 70% CPU và 50 ms p99 latency. Marketing dự báo tải trọng đỉnh là 4,000 req/s trong giờ cao điểm. Một sự kiện quảng cáo có thể tăng lên 3x đỉnh điểm trong thời gian ngắn. Bạn muốn dịch vụ sống sót được 3 sự cố bản sao đồng thời mà không vượt quá 80% sử dụng trên những bản sao còn lại.
Thời gian khởi động lạnh, chảy chậm và các cạnh thực tế khác
Hạm đội thực có các cạnh thực tế
Công thức giả định bản sao xuất hiện ngay lập tức, chấp nhận lưu lượng ngay lập tức và loại bỏ lưu lượng ngay lập tức. Không có điều đó đúng trong sản xuất.
Bắt đầu lạnh: một bản sao mới cần khởi động hệ điều hành, bắt đầu quá trình, tải cấu hình, ấm các cache và kiểm tra sức khỏe. Từ 5 giây (khởi động lại container) đến 5 phút (boot đầy đủ VM + tải hình ảnh). Autoscaling không thể đáp ứng những đợt tăng đột ngột ngắn hơn thời gian trì hoãn này.
Lưu lượng chậm: một bản sao đang được loại bỏ khỏi nhóm cần thời gian để hoàn thành các yêu cầu đang diễn ra trước khi kết thúc. Nếu không, người dùng sẽ thấy các phản hồi bị cắt ngắn. Các proxy ngược hỗ trợ thoát (dừng chấp nhận các yêu cầu mới, hoàn thành các yêu cầu đang hoạt động) nhưng nó mất vài giây đến vài phút.
Bể ấm: các đội sản xuất giữ một nhóm bản sao đã chuẩn bị sẵn nhưng không hoạt động để sẵn sàng nhận traffic trên tín hiệu. Thương lượng một chi phí ổn định nhỏ cho phản ứng nở nhanh.
Rút lui kết nối vs giết ngay lập tức: tắt máy êm ái quan trọng. Một SIGTERM kích hoạt thoát mất thời gian hơn SIGKILL nhưng không làm đứt gãy các yêu cầu của người dùng.
Cửa sổ kiểm tra sức khỏe: một bản sao mới bắt đầu có thể vượt qua kiểm tra sức khỏe đầu tiên trước khi nhóm kết nối cơ sở dữ liệu của nó ấm; proxy sau đó gửi traffic thực và các yêu cầu đầu tiên là chậm. Tune kiểm tra sức khỏe để kiểm tra con đường thực, không chỉ sự sống của quá trình.
Tích tụ tính liên kết: ngay cả các tầng được coi là không trạng thái cũng tích tụ tính liên kết theo thời gian (caches CDN, caches DNS, các nhóm kết nối). Hãy nghi ngờ về 'bản sao giống nhau' mà không sao behaving khác nhau.
Bể Ấm hay Autoscaling Phản ứng?
API metadata video của bạn (cùng một API từ câu hỏi trước, được thiết kế với 51 bản sao cho đỉnh thường + nở) trải qua một đợt nở 30 giây lên 5 lần mức bình thường mỗi khi một video viral mới được tải lên. Autoscaling hiện tại cần 90 giây để thêm một bản sao từ trạng thái lạnh (pull hình ảnh + làm ấm). Trong khoảng thời gian 90 giây, latency tăng cao và một số yêu cầu bị timeout.
Thiết kế một tầng không trạng thái dưới các hạn chế
Tích hợp
Bạn đã học lý do vì sao tăng kích thước ngang sẽ thắng sau một ngưỡng nhỏ, điều gì là trạng thái trong thực tế, cách tính kích thước một đội tàu dưới tải trọng dự kiến và bùng nổ, & nơi tăng kích thước ngang bị phá vỡ ở các cạnh.
Áp dụng tất cả bốn.
Thiết kế một tier backend cho feed.example.com, một API social-feed. Các ràng buộc: khả năng mỗi bản sao 200 req / s ở 70% CPU; tải dự kiến cao nhất 1500 req / s; hệ số bùng nổ 2,5x (các câu chuyện trending occasional); chịu đựng được 2 sự cố đồng thời replica; thời gian khởi động lạnh 60 giây; đợt bùng nổ có thể kéo dài 45 giây; ngân sách cho phép một lượng idle capacity nhưng không phải là 2,5x cung cấp vĩnh viễn.
Hướng Sau Bài Học Này
Hướng Sau Bài Học Này
Bạn bây giờ có một mô hình tư tưởng làm việc của tier không trạng thái: tại sao nó mở rộng, cách tính kích thước, điều gì bị phá vỡ ở các cạnh và nơi trạng thái phải di chuyển khi bạn đẩy nó ra khỏi lớp cần tăng lên.
Bài học tiếp theo trong khóa học này (cs_distsys_ingress_egress_separation) giải quyết vấn đề tinh tế: ngay cả tier không trạng thái được kích cỡ hoàn hảo cũng có thể thất bại theo cách bất ngờ khi lưu lượng truy cập vào và lưu lượng truy cập ra cùng sử dụng đường truyền mạng giống nhau. Ví dụ cổ điển liên quan đến một proxy cố gắng kết nối với chính nó; cách giải quyết liên quan đến việc chia một tier thành hai với trách nhiệm khác nhau.
Bài học kèm theo: geometry_of_stateless_horizontal_scaling deriving the queueing curve, Little's Law applied to a replica fleet, & the geometric meaning of the 80% utilization knee.
Chúc mừng. Tiến lên.