DevOps Kiến Thức Linux Proxy

Pingora là gì ? Những điều cần biết về Proxy Pingora của Cloudflare

pingora-logo

Pingora là gì

Pingora là một framework được xây dựng dựa trên ngôn ngữ Rust để xây dựng các hệ thống nối mạng nhanh, đáng tin cậy và có thể lập trình được.

Pingora đã được thử nghiệm trong chiến đấu vì nó đã phục vụ hơn 40 triệu yêu cầu Internet mỗi giây trong hơn một vài năm.

Giới thiệu


Hôm nay, chúng ta sẽ nói về Pingora, một proxy HTTP mới mà Cloudflare đã xây dựng nội bộ bằng Rust, phục vụ hơn 1 nghìn tỷ yêu cầu mỗi ngày, tăng hiệu suất của Cloudflare và hỗ trợ nhiều tính năng mới cho khách hàng, trong khi chỉ yêu cầu một phần ba tài nguyên CPU và bộ nhớ của cơ sở hạ tầng proxy trước đây.

Khi Cloudflare mở rộng quy mô, họ đã sử dụng NGINX. Nó tuyệt vời trong nhiều năm, nhưng theo thời gian, những hạn chế ở quy mô của Cloudflare có nghĩa là việc xây dựng một cái gì đó mới có ý nghĩa hơn. Cloudflare không còn có thể đạt được hiệu suất cần thiết và NGINX cũng không có các tính năng cần thiết cho môi trường rất phức tạp của họ.

Nhiều khách hàng và người dùng của Cloudflare sử dụng mạng toàn cầu Cloudflare làm proxy giữa các máy khách HTTP (chẳng hạn như trình duyệt web, ứng dụng, thiết bị IoT, v.v.) và máy chủ. Trước đây, công ty Cloudflare đã nói rất nhiều về cách các trình duyệt và tác nhân người dùng khác kết nối với mạng của họ và Cloudflare đã phát triển nhiều công nghệ cũng như triển khai các giao thức mới (xem QUIC và tối ưu hóa cho http2) để tạo nên nhánh kết nối này, mang lại hiệu quả hơn.

Hôm nay, chúng tôi đang tập trung vào một phần khác của phương trình: dịch vụ ủy quyền lưu lượng truy cập giữa mạng và máy chủ của chúng tôi trên Internet. Dịch vụ proxy này hỗ trợ CDN, Tìm nạp công nhân, Đường hầm, Luồng, R2 và nhiều tính năng và sản phẩm khác của chúng tôi.

Hãy cùng tìm hiểu lý do tại sao chúng tôi chọn thay thế dịch vụ cũ của mình và cách chúng tôi phát triển Pingora, hệ thống mới được thiết kế đặc biệt cho các trường hợp và quy mô sử dụng của khách hàng Cloudflare.

Tại sao lại xây dựng một proxy khác


Trong những năm qua, việc sử dụng NGINX của chúng tôi đã gặp phải những hạn chế. Đối với một số hạn chế, chúng tôi đã tối ưu hóa hoặc giải quyết chúng. Nhưng những người khác khó vượt qua hơn nhiều.

Hạn chế về kiến trúc ảnh hưởng đến hiệu suất


Kiến trúc (quy trình) công nhân NGINX có những hạn chế về vận hành đối với các trường hợp sử dụng của chúng tôi, làm ảnh hưởng đến hiệu suất và hiệu quả của chúng tôi.

Đầu tiên, trong NGINX, mỗi yêu cầu chỉ có thể được phục vụ bởi một nhân viên duy nhất. Điều này dẫn đến tải không cân bằng trên tất cả các lõi CPU, dẫn đến tốc độ chậm.

Do hiệu ứng ghim quy trình yêu cầu này, các yêu cầu làm nặng CPU hoặc chặn các tác vụ IO có thể làm chậm các yêu cầu khác. Như những bài đăng trên blog đó chứng thực, chúng tôi đã dành rất nhiều thời gian để giải quyết những vấn đề này.

Vấn đề quan trọng nhất đối với các trường hợp sử dụng của chúng tôi là việc tái sử dụng kết nối kém. Máy của chúng tôi thiết lập kết nối TCP tới máy chủ gốc để đáp ứng các yêu cầu HTTP proxy. Việc tái sử dụng kết nối sẽ tăng tốc các yêu cầu TTFB (thời gian đến byte đầu tiên) bằng cách sử dụng lại các kết nối đã thiết lập trước đó từ nhóm kết nối, bỏ qua các bắt tay TCP và TLS cần thiết trên kết nối mới.

Tuy nhiên, nhóm kết nối NGINX là dành cho mỗi công nhân. Khi một yêu cầu đến một công nhân nhất định, nó chỉ có thể sử dụng lại các kết nối bên trong công nhân đó. Khi chúng tôi thêm nhiều nhân viên NGINX hơn để mở rộng quy mô, tỷ lệ sử dụng lại kết nối của chúng tôi sẽ kém hơn do các kết nối nằm rải rác trên nhiều nhóm riêng biệt hơn của tất cả các quy trình. Điều này dẫn đến TTFB chậm hơn và cần duy trì nhiều kết nối hơn, điều này tiêu tốn tài nguyên (và tiền bạc) cho cả chúng tôi và khách hàng của chúng tôi.

image 74

Như đã đề cập trong các bài đăng trên blog trước đây, chúng tôi có giải pháp cho một số vấn đề này. Nhưng nếu chúng ta có thể giải quyết vấn đề cơ bản: mô hình công nhân/quy trình, chúng ta sẽ giải quyết tất cả những vấn đề này một cách tự nhiên.

Một số loại chức năng khó thêm vào


NGINX là một máy chủ web, bộ cân bằng tải hoặc một cổng đơn giản rất tốt. Nhưng Cloudflare còn làm được nhiều hơn thế. Chúng tôi đã từng xây dựng tất cả chức năng cần thiết xung quanh NGINX, điều này không dễ thực hiện khi cố gắng không khác biệt quá nhiều với cơ sở mã ngược dòng NGINX.

Ví dụ: khi thử lại/không thực hiện được một yêu cầu, đôi khi chúng tôi muốn gửi yêu cầu đến một máy chủ gốc khác với một bộ tiêu đề yêu cầu khác. Nhưng đó không phải là điều NGINX cho phép chúng tôi làm. Trong những trường hợp như thế này, chúng tôi dành thời gian và công sức để giải quyết các ràng buộc NGINX.

Trong khi đó, các ngôn ngữ lập trình mà chúng tôi phải làm việc không giúp giảm bớt khó khăn. NGINX hoàn toàn bằng C, thiết kế này không đảm bảo an toàn cho bộ nhớ. Rất dễ xảy ra lỗi khi làm việc với cơ sở mã của bên thứ 3 như vậy. Khá dễ dàng gặp phải các vấn đề về an toàn bộ nhớ, ngay cả đối với các kỹ sư có kinh nghiệm và chúng tôi muốn tránh những vấn đề này càng nhiều càng tốt.

Ngôn ngữ khác mà chúng tôi sử dụng để bổ sung cho C là Lua. Nó ít rủi ro hơn nhưng cũng kém hiệu quả hơn. Ngoài ra, chúng tôi thường thấy mình thiếu tính năng gõ tĩnh khi làm việc với mã Lua và logic nghiệp vụ phức tạp.

Và cộng đồng NGINX không hoạt động nhiều và sự phát triển có xu hướng “đằng sau cánh cửa đóng kín”.

Lựa chọn xây dựng cho riêng mình


Trong vài năm qua, khi chúng tôi tiếp tục phát triển cơ sở khách hàng và bộ tính năng của mình, chúng tôi liên tục đánh giá ba lựa chọn:

Tiếp tục đầu tư vào NGINX và có thể phân nhánh nó để điều chỉnh 100% theo nhu cầu của chúng tôi. Chúng tôi có chuyên môn cần thiết, nhưng do những hạn chế về kiến trúc nêu trên, nên cần phải có nỗ lực đáng kể để xây dựng lại kiến trúc theo cách hỗ trợ đầy đủ nhu cầu của chúng tôi.
Di chuyển sang cơ sở mã proxy bên thứ 3 khác. Chắc chắn có những dự án tốt, như sứ giả và những dự án khác. Nhưng con đường này có nghĩa là chu kỳ tương tự có thể lặp lại sau một vài năm.
Bắt đầu với một phương án rõ ràng, xây dựng nền tảng và khuôn khổ nội bộ. Sự lựa chọn này đòi hỏi sự đầu tư ban đầu nhất về nỗ lực kỹ thuật.
Chúng tôi đã đánh giá từng lựa chọn này mỗi quý trong vài năm qua. Không có công thức rõ ràng nào để biết lựa chọn nào là tốt nhất. Trong vài năm, chúng tôi tiếp tục con đường ít trở ngại nhất, tiếp tục tăng cường NGINX. Tuy nhiên, tại một thời điểm nào đó, việc xây dựng lợi tức đầu tư cho proxy của riêng chúng tôi dường như đáng giá. Chúng tôi đã kêu gọi xây dựng proxy từ đầu và bắt đầu thiết kế ứng dụng proxy mà chúng tôi mơ ước.

Dự án Pingora

Quyết định thiết kế


Để tạo một proxy phục vụ hàng triệu yêu cầu mỗi giây một cách nhanh chóng, hiệu quả và an toàn, trước tiên chúng ta phải đưa ra một số quyết định thiết kế quan trọng.

Chúng tôi chọn Rust làm ngôn ngữ của dự án vì nó có thể thực hiện những gì C có thể làm theo cách an toàn cho bộ nhớ mà không ảnh hưởng đến hiệu suất.

Mặc dù có một số thư viện HTTP tuyệt vời có sẵn của bên thứ 3, chẳng hạn như hyper, nhưng chúng tôi đã chọn xây dựng thư viện của riêng mình vì muốn tối đa hóa tính linh hoạt trong cách xử lý lưu lượng HTTP và để đảm bảo rằng chúng tôi có thể đổi mới theo tốc độ của riêng mình.

Tại Cloudflare, chúng tôi xử lý lưu lượng truy cập trên toàn bộ Internet. Chúng tôi có nhiều trường hợp lưu lượng truy cập HTTP kỳ lạ và không tuân thủ RFC mà chúng tôi phải hỗ trợ. Đây là một vấn đề nan giải phổ biến trên cộng đồng HTTP và web, nơi có sự căng thẳng giữa việc tuân thủ nghiêm ngặt các thông số kỹ thuật HTTP và việc đáp ứng các sắc thái của một hệ sinh thái rộng lớn gồm các máy khách hoặc máy chủ có khả năng kế thừa. Chọn một bên có thể là một công việc khó khăn.

Mã trạng thái HTTP được xác định trong RFC 9110 dưới dạng số nguyên có ba chữ số và thường nằm trong khoảng từ 100 đến 599. Hyper là một trong những cách triển khai như vậy. Tuy nhiên, nhiều máy chủ hỗ trợ sử dụng mã trạng thái trong khoảng từ 599 đến 999. Một vấn đề đã được tạo ra cho tính năng này nhằm khám phá nhiều khía cạnh khác nhau của cuộc tranh luận. Mặc dù nhóm Hyper cuối cùng đã chấp nhận thay đổi đó, nhưng sẽ có lý do chính đáng để họ từ chối yêu cầu như vậy và đây chỉ là một trong nhiều trường hợp về hành vi không tuân thủ mà chúng tôi cần hỗ trợ.

Để đáp ứng các yêu cầu về vị trí của Cloudflare trong hệ sinh thái HTTP, chúng tôi cần một thư viện HTTP mạnh mẽ, dễ dàng tùy chỉnh, có thể tồn tại trong môi trường khắc nghiệt của Internet và hỗ trợ nhiều trường hợp sử dụng không tuân thủ. Cách tốt nhất để đảm bảo điều đó là thực hiện chính chúng ta.

Quyết định thiết kế tiếp theo xoay quanh hệ thống lập kế hoạch khối lượng công việc của chúng tôi. Chúng tôi đã chọn đa luồng thay vì đa xử lý để chia sẻ tài nguyên, đặc biệt là nhóm kết nối, một cách dễ dàng. Chúng tôi cũng quyết định rằng việc đánh cắp công việc là cần thiết để tránh một số loại vấn đề về hiệu suất được đề cập ở trên. Thời gian chạy không đồng bộ của Tokio hóa ra lại rất phù hợp với nhu cầu của chúng tôi.

Cuối cùng, chúng tôi muốn dự án của mình trực quan và thân thiện với nhà phát triển. Những gì chúng tôi xây dựng không phải là sản phẩm cuối cùng và có thể mở rộng dưới dạng nền tảng vì có nhiều tính năng hơn được xây dựng trên nền tảng đó. Chúng tôi đã quyết định triển khai giao diện lập trình dựa trên sự kiện “tuổi thọ của yêu cầu” tương tự như NGINX/OpenResty. Ví dụ: giai đoạn “bộ lọc yêu cầu” cho phép nhà phát triển chạy mã để sửa đổi hoặc từ chối yêu cầu khi có yêu cầu.

Pingora sản xuất nhanh hơn


Hãy tua nhanh đến hiện tại. Pingora xử lý hầu hết mọi yêu cầu HTTP cần tương tác với máy chủ gốc (chẳng hạn như lỗi bộ nhớ đệm) và chúng tôi đã thu thập rất nhiều dữ liệu hiệu suất trong quá trình này.

Trước tiên, hãy xem Pingora tăng tốc lưu lượng truy cập của khách hàng như thế nào. Tổng lưu lượng truy cập trên Pingora cho thấy mức giảm 5 mili giây trên TTFB trung bình và giảm 80 mili giây ở phân vị thứ 95. Điều này không phải vì chúng tôi chạy mã nhanh hơn. Ngay cả dịch vụ cũ của chúng tôi cũng có thể xử lý các yêu cầu trong phạm vi dưới một phần nghìn giây.

Khoản tiết kiệm được đến từ kiến trúc mới của chúng tôi, kiến trúc có thể chia sẻ kết nối trên tất cả các luồng. Điều này có nghĩa là tỷ lệ tái sử dụng kết nối tốt hơn, tiêu tốn ít thời gian hơn cho quá trình bắt tay TCP và TLS.

image 75

Đối với tất cả khách hàng, Pingora chỉ tạo ra số lượng kết nối mới mỗi giây bằng một phần ba so với dịch vụ cũ. Đối với một khách hàng lớn, nó đã tăng tỷ lệ tái sử dụng kết nối từ 87,1% lên 99,92%, giúp giảm 160 lần kết nối mới về nguồn gốc. Để trình bày con số một cách trực quan hơn, bằng cách chuyển sang Pingora, chúng tôi đã tiết kiệm cho khách hàng và người dùng của mình 434 năm thời gian bắt tay mỗi ngày.

Nhiều tính năng hơn


Việc có một giao diện thân thiện với nhà phát triển mà các kỹ sư đã quen thuộc đồng thời loại bỏ các hạn chế trước đó cho phép chúng tôi phát triển nhiều tính năng hơn, nhanh hơn. Chức năng cốt lõi như các giao thức mới đóng vai trò là khối xây dựng cho nhiều sản phẩm hơn mà chúng tôi có thể cung cấp cho khách hàng.

Ví dụ: chúng tôi có thể thêm hỗ trợ ngược dòng HTTP/2 cho Pingora mà không gặp trở ngại lớn. Điều này cho phép chúng tôi cung cấp gRPC cho khách hàng của mình ngay sau đó. Việc thêm chức năng tương tự này vào NGINX sẽ đòi hỏi nhiều nỗ lực kỹ thuật hơn đáng kể và có thể chưa thành hiện thực.

Gần đây hơn, chúng tôi đã công bố Dự trữ bộ đệm trong đó Pingora sử dụng bộ lưu trữ R2 làm lớp bộ đệm. Khi chúng tôi bổ sung thêm nhiều chức năng hơn cho Pingora, chúng tôi có thể cung cấp các sản phẩm mới mà trước đây không khả thi.

Hiệu quả hơn


Trong quá trình sản xuất, Pingora tiêu thụ CPU ít hơn khoảng 70% và bộ nhớ ít hơn 67% so với dịch vụ cũ của chúng tôi có cùng lưu lượng tải. Khoản tiết kiệm đến từ một số yếu tố.

Mã Rust của chúng tôi chạy hiệu quả hơn so với mã Lua cũ của chúng tôi. Trên hết, còn có sự khác biệt về hiệu quả so với kiến trúc của chúng. Ví dụ: trong NGINX/OpenResty, khi mã Lua muốn truy cập tiêu đề HTTP, nó phải đọc mã đó từ cấu trúc NGINX C, phân bổ chuỗi Lua rồi sao chép nó vào chuỗi Lua. Sau đó, Lua cũng phải thu gom chuỗi mới của nó. Trong Pingora, nó chỉ là truy cập chuỗi trực tiếp.

Mô hình đa luồng cũng giúp việc chia sẻ dữ liệu qua các yêu cầu hiệu quả hơn. NGINX cũng có bộ nhớ dùng chung nhưng do hạn chế về triển khai, mọi quyền truy cập bộ nhớ dùng chung đều phải sử dụng khóa mutex và chỉ có thể đưa các chuỗi và số vào bộ nhớ dùng chung. Trong Pingora, hầu hết các mục được chia sẻ đều có thể được truy cập trực tiếp thông qua các tài liệu tham khảo được chia sẻ phía sau bộ đếm tham chiếu nguyên tử.

Một phần đáng kể khác của việc tiết kiệm CPU, như đã đề cập ở trên, là tạo ra ít kết nối mới hơn. Bắt tay TLS đắt tiền so với việc chỉ gửi và nhận dữ liệu thông qua các kết nối đã thiết lập.

An toàn hơn


Tính năng vận chuyển nhanh chóng và an toàn là điều khó khăn, đặc biệt là ở quy mô của chúng tôi. Thật khó để dự đoán mọi trường hợp biên có thể xảy ra trong môi trường phân tán xử lý hàng triệu yêu cầu mỗi giây. Phân tích mờ và tĩnh chỉ có thể giảm thiểu rất nhiều. Ngữ nghĩa an toàn cho bộ nhớ của Rust bảo vệ chúng tôi khỏi hành vi không xác định và giúp chúng tôi tin tưởng rằng dịch vụ của chúng tôi sẽ chạy chính xác.

Với những đảm bảo đó, chúng tôi có thể tập trung hơn vào việc thay đổi dịch vụ của chúng tôi sẽ tương tác như thế nào với các dịch vụ khác hoặc nguồn gốc của khách hàng. Chúng tôi có thể phát triển các tính năng ở nhịp độ cao hơn và không bị gánh nặng bởi sự an toàn của bộ nhớ cũng như các sự cố khó chẩn đoán.

Khi xảy ra sự cố, kỹ sư cần dành thời gian để chẩn đoán xem sự cố đã xảy ra như thế nào và nguyên nhân gây ra sự cố. Kể từ khi thành lập Pingora, chúng tôi đã phục vụ hàng trăm nghìn tỷ yêu cầu và vẫn chưa gặp sự cố do mã dịch vụ của chúng tôi.

Trên thực tế, sự cố Pingora rất hiếm nên chúng tôi thường tìm thấy các sự cố không liên quan khi gặp phải sự cố. Gần đây, chúng tôi đã phát hiện ra lỗi kernel ngay sau khi dịch vụ của chúng tôi bắt đầu gặp sự cố. Chúng tôi cũng đã phát hiện ra các sự cố phần cứng trên một số máy, trước đây đã loại trừ các lỗi bộ nhớ hiếm gặp do phần mềm của chúng tôi gây ra ngay cả khi gần như không thể gỡ lỗi đáng kể.

Phần kết luận


Tóm lại, chúng tôi đã xây dựng một proxy nội bộ nhanh hơn, hiệu quả hơn và linh hoạt hơn làm nền tảng cho các sản phẩm hiện tại và tương lai của chúng tôi.

Chúng tôi sẽ quay lại với nhiều chi tiết kỹ thuật hơn liên quan đến các vấn đề chúng tôi gặp phải, các tối ưu hóa chúng tôi đã áp dụng và các bài học chúng tôi đã học được từ việc xây dựng Pingora và triển khai nó để cung cấp năng lượng cho một phần đáng kể của Internet. Chúng tôi cũng sẽ quay lại với kế hoạch mở nguồn của nó.

Pingora là nỗ lực mới nhất của chúng tôi trong việc viết lại hệ thống của mình nhưng đây sẽ không phải là nỗ lực cuối cùng của chúng tôi. Nó cũng chỉ là một trong những khối xây dựng trong việc tái kiến trúc hệ thống của chúng tôi.

Tham khảo

  • https://github.com/cloudflare/pingora
  • https://blog.cloudflare.com/pingora-open-source

Đăng ký liền tay Nhận Ngay Bài Mới

Subscribe ngay

Cám ơn bạn đã đăng ký !

Lỗi đăng ký !

Tags

Add Comment

Click here to post a comment

Đăng ký liền tay
Nhận Ngay Bài Mới

Subscribe ngay

Cám ơn bạn đã đăng ký !

Lỗi đăng ký !