Tam's Blog

<- Quay về trang chủ

gRPC Load balancing (1)

Khi thị trường có xu hướng chuyển dần từ monolithic sang microservice, bài toán giao tiếp giữa các service trở nên rất quan trọng, với những service thông thường hiện nay, mình thấy có 3 cách giao tiếp phổ biến:

Khi triển khai hệ thống cần xử lý một lượng tải lớn, mỗi loại service sẽ cần phải chạy rất nhiều instance, vậy bài toán đặt ra làm thế nào để chia tải giữa các instance? Độ hiệu quả cũng như chi phí cài đặt, bảo trì như thế nào? Phương pháp áp dụng cho mỗi protocol có khác nhau không?

Để trả lời những câu hỏi trên, mình sẽ viết một chuỗi bài tìm hiểu về cân bằng tải gRPC và các phương pháp hiện thực trong thực tế. Thông qua chuỗi bài viết này, mục tiêu được đặt ra là mình và bạn đọc sẽ hiểu rõ và vận dụng được những kiến thức cân bằng tải gRPC dự án thực tế, cụ thể:

Phương pháp

Hiện tại có một vài phương pháp phổ biến để xử lý cân bằng tải cho grPC:

Mỗi phương pháp sẽ có ưu nhược điểm riêng cũng như chi phí cài đặt, bảo trì khác nhau.

Tuy nhiên, trước hết hãy cùng mình tìm hiểu một số kiến thức cơ bản cần nắm rõ như HTTP/2, long-lived TCP connection,...

HTTP/2

HTTP/2 cho phép client và server gửi/nhận cùng lúc nhiều request/response trên cùng 1 TCP connection (multiplexing), phân biệt với nhau dựa trên logical stream.

http/2

HTTP/2 ra đời giản quyết vấn đề head-of-line_blocking của HTTP/1.1, ở phiên bản cũ, mặc dù chúng ta có thể tái sử dụng connection, nhưng những requests được xử lý tuần tự, có thể gây hiện tượng thắt cổ chai nếu có nhiều request trên cùng 1 connection.

Với tính chất này, nếu không có yêu cầu gì đặc biệt, chúng ta sẽ có nhu cầu tái sử dụng connection của HTTP/2 để giảm thiểu chi phí khởi tạo connection, do đó, TCP connection trong HTTP/2 sẽ là long-lived connection.

gRPC load balancing

Load balancer as proxy

Transport layer (layer 4)

Khi cân bằng tải ở tầng transport, LB sẽ làm việc với các gói tin TCP, một khi client khởi tạo connection tới LB, nó sẽ tạo 1 connection tương ứng đến một backend server rồi chuyển tiếp tất cả gói tin dựa trên sự ánh xạ này, sự ánh xạ này sẽ được giữ cho đến khi connection bị đóng, từ tổng thể, 2 connection được tạo ra có thể được xem là 1 persistent connection.

grpc-connection-load-balancing

Do tính chất persistent connection, LB sẽ cân bằng tải cho quá trình thiết lập connection, một khi connection đã được tạo, tất cả request sẽ được gửi thông nó, mình gọi nó là connection-based load balancing. Ở ví dụ như hình trên, nếu client chỉ tạo 1 connection thì sẽ gây ra hiện tượng server instance 2 hoặc server instance 3 ở trạng thái "thư giãn".

Để giải quyết vấn đề này, chúng ta sẽ sử dụng kĩ thuật pooling ở phía client, mục đích là tạo nhiều connections thông qua LB và sử dụng chúng để gửi request. Khi pool được tạo:

Tuy nhiên, chi phí để hiện thực ở phía client sẽ cao hơn vì các lý do:

grpc-loadbalacning-lb-proxy

Điều gì sẽ xảy ra khi scale server?

Khi một server mới được thêm vào cụm backend, nếu pool ở client của chúng ta đã đạt đến số connection tối đa thì cách làm này gặp vấn đề lớn, sẽ không có connection mới nào được khởi tạo đến server mới và dẫn đến sự quá tải ở các server đang có, dẫn đến sập server nếu số lượng request tăng. Để giải quyết vấn đề này, chúng ta cần có cơ chế refresh pool, mỗi connection trong pool sẽ có 1 thời gian sống nhất định, client sẽ chạy 1 job để refresh pool theo cơ chế như:

Điều này đảm bảo connection sẽ được chia tải đều đến các server, tuy nhiên chúng ta cần tính toán kĩ những số liệu trên dựa trên đặc điểm chịu tải của từng service, việc này có thể được làm thông qua quá trình benchmark hệ thống.

Application layer (layer 7)

Khi LB hoạt động ở tầng application, nó sẽ sử dụng các thông tin về request để cân bằng tải, cũng với ví dụ client tạo 1 connection tới LB như ở trên rồi gửi requests, lúc này LB sẽ cân bằng tải từng request một tới các backend server dựa trên các thuật toán được cấu hình.

/grpc-loadbalacning-L7

Ở tầng application, LB sử dụng được nhiều thông tin hơn để cân bằng tải, do đó có thể hỗ trợ các thuật toán cân bằng tải phức tạp. Tuy nhiên, vì cần phải xem nội dung của request, hiệu năng lúc này sẽ giảm xuống so với trường hợp LB hoạt động ở tầng transport.

Không sử dụng load balancer?

Đối với những hệ thống yêu cầu khắt khe về hiệu năng, sử dụng load balancer có lẽ không phải là giải pháp tốt. Client có một lợi thế khi sử dụng load balancer là nó không cần phải quan tâm đến địa chỉ IP cụ thể của backend hay những thứ khác liên quan đến hạ tầng, tất cả những thứ nó cần phải biết là địa chỉ của load balancer, nếu chúng ta không sử dụng load balancer, một vấn đề mới xuất hiện, làm thế nào để client và server tìm thấy nhau?

Đây là câu hỏi kinh điển gắn liền với thuật ngữ service discovery, có một service thứ 3 đứng ra làm cầu nối giữa client và server, service này lưu thông tin của server và trả lời mỗi khi client hỏi hoặc chủ động thông báo mỗi khi có sự thay đổi. Khi client có được địa chỉ của các server thông qua service thứ 3 này, nó sẽ khởi tạo connection trực tiếp đến các server và chia tải request trên các connection này, việc load balancing đã trở thành client-side load balacing, gRPC client đang đảm nhiệm việc cân bằng tải, khi mình nói đến gRPC client, tức là việc xử lý này sẽ được xử lý bởi gRPC, lập trình viên không cần hiện thực thêm gì.

grpc-service-discovery

Ý tưởng này có thể được hiện thực thông qua nhiều mô hình:

Để tăng hiệu năng cũng như throughput về mặt số lượng request, chúng ta cũng có thể áp dụng kĩ thuật pooling ở phía client cho phương pháp này.

Tổng kết

Ở bài viết này, mình đã phân tích ý tưởng load balancing grpc, có 2 điều cần hiểu rõ để tránh mơ hồ trong lúc hiện thực các phương pháp này:

Ở các bài tiếp theo, mình sẽ đi vào hiện thực và kiểm thử các phương pháp cân bằng tải gRPC để làm rõ hơn phần lý thuyết này.

Tham khảo