Bài 1

Cùng một bộ route, ba gateway chọn khác nhau

Envoy, Kong và OpenResty đều nhận một danh sách route rồi chọn ra một cái cho mỗi request. Nghe như cùng một việc, nhưng ba bên định nghĩa “khớp” khác nhau và sắp thứ tự ưu tiên khác nhau. Bài này dựng đúng một bộ năm route trên cả ba, gửi cùng bảy đường dẫn qua, và ghi lại route nào thắng.

Bộ đo

Bốn container trên một mạng Docker. Backend là nginx trả về đúng đường dẫn nó nhận được, nên mỗi route được cấu hình để viết lại đường dẫn thành một dấu hiệu riêng — đọc phản hồi là biết route nào đã thắng.

server {
  listen 8080;
  default_type text/plain;
  location / { return 200 "$request_uri\n"; }
}

Cả ba gateway nhận cùng năm route:

loạimẫu
r1prefix/a
r2khớp đúng/a/b
r3prefix/a/b/c
r4regex/a/.*\.png$
r5prefix/

Kong không có khái niệm “khớp đúng”, nên r2 ở Kong là một regex có neo hai đầu. Phiên bản đã đo: Envoy 1.31.10, Kong 3.9.3 chạy DB-less, OpenResty 1.31.1.1.

“Regex” của ba bên là ba thứ khác nhau

Trước khi nói tới thứ tự ưu tiên, có một khác biệt sâu hơn: cùng một chuỗi mẫu không khớp cùng một tập đường dẫn. Đo bằng đúng một route regex /b$ cộng một route bắt tất cả:

mẫuđường dẫnEnvoyKongOpenResty
/b$/bkhớpkhớpkhớp
/a/bkhôngkhôngkhớp
/xbkhôngkhôngkhông
/b/bkhớpkhớpkhớp
/b/ckhôngkhớpkhớp
/a/bkhôngkhôngkhớp

Ba dòng đó đủ để đọc ra ba quy tắc:

Chép nguyên mẫu regex từ gateway này sang gateway kia là sai Đây không phải chuyện chi tiết. Một mẫu chặn quyền truy cập, ví dụ /admin, chuyển từ nginx sang Envoy sẽ thôi khớp /admin/users; chuyển ngược lại thì bắt luôn /v1/admin. Khi chuyển, phải viết lại mẫu cho đúng ngữ nghĩa nơi đến rồi thử lại bằng chính đường dẫn thật.

Envoy không sắp lại gì cả

Với Envoy, route thắng là route đầu tiên khớp trong đúng thứ tự viết. Không có bước sắp xếp nào. Viết cùng năm route ở ba thứ tự khác nhau cho ba bảng khác hẳn nhau:

đường dẫnviết r1→r5viết r5→r1viết r4,r3,r2,r1,r5viết r2,r3,r4,r1,r5
/ar1r5r1r1
/a/br1r5r2r2
/a/b/cr1r5r3r3
/a/b/c/dr1r5r3r3
/a/x.pngr1r5r4r4
/a/b/c/x.pngr1r5r4r3
/zr5r5r5r5

Cột thứ hai là trường hợp xấu nhất: viết route bắt tất cả lên đầu làm bốn route còn lại thành vô dụng, và Envoy khởi động bình thường, không một dòng cảnh báo. Cột cuối tinh vi hơn: chỉ vì prefix /a/b/c viết trước regex .png mà /a/b/c/x.png đi sang route khác.

Cùng phép thử đó trên Kong và OpenResty: viết xuôi hay viết ngược đều cho y hệt một bảng. Cả hai đều sắp lại danh sách trước khi chạy.

Kong và nginx sắp theo cái gì

Với bộ năm route ở trên, Kong và OpenResty cho cùng một kết quả:

đường dẫnroute thắngvì sao
/ar1chỉ prefix /a và / khớp; lấy cái dài hơn
/a/br2khớp đúng (Kong: regex có neo hai đầu)
/a/b/cr3prefix dài nhất
/a/b/c/dr3prefix dài nhất
/a/x.pngr4regex thắng prefix
/a/b/c/x.pngr4regex thắng cả prefix dài hơn
/zr5chỉ còn route bắt tất cả

Dòng áp chót là chỗ đáng chú ý: regex thắng prefix bất kể prefix dài bao nhiêu. Đo riêng trên Kong với prefix /a/b/c/d/e và regex /a/.*\.png$: /a/b/c/d/e/x.png đi vào regex, còn /a/b/c/d/e/x.txt mới rơi về prefix. nginx cũng vậy, và ở nginx có ^~ để chặn bước regex — Kong và Envoy không có công cụ tương đương.

Giữa hai regex, Kong chọn cái nào là chuyện không đoán được Hai route ~/a/b/.*$ và ~/a/.*\.png$ cùng khớp /a/b/x.png. Đo với hai thứ tự viết ngược nhau: cả hai lần Kong đều chọn ~/a/b/.*$, dù mẫu kia dài hơn. Nghĩa là không suy ra được từ thứ tự viết, cũng không từ độ dài mẫu. Đặt regex_priority: 10 cho mẫu .png thì nó thắng — đó là công cụ duy nhất kiểm soát được, và nên đặt rõ mỗi khi hai regex có thể chồng nhau.

Hai route trùng nhau thì sao

Viết hai route cùng một mẫu /a, trỏ tới hai backend khác nhau:

gatewaykết quả
OpenRestykhông khởi động: [emerg] duplicate location "/a"
Envoykhởi động bình thường, route viết trước chạy
Kongkhởi động bình thường, và không phải route viết trước chạy

Hàng cuối cần nói rõ. Đo ba cặp: route tên k1 viết trước, k2 viết sau thì k2 thắng; zz trước aa sau thì zz thắng; đảo lại aa trước zz sau thì vẫn zz thắng. Nghĩa là người thắng đi theo tên route chứ không theo thứ tự viết — mà tên thì không ai chọn với ý định quyết định ưu tiên. Chỉ nginx từ chối khởi động; hai gateway kia im lặng chọn một cái.

Phòng thí nghiệm

Gõ bộ route của bạn và những đường dẫn cần thử, rồi xem ba gateway trả lời thế nào. Dòng nào ba bên không đồng ý sẽ được tô vàng. Thuật toán là bản viết lại của cả ba và đã khớp 74/74 kết quả curl trên bộ đo ở trên.

Danh sách route

    Route nào thắng

      Nhớ gì

      Bài tiếp theo hỏi câu còn lại: sau khi đã chọn được route, backend nhận được đường dẫn gì.