Chi phí tiềm ẩn của JSON trong APIs
Nếu bạn đã xây dựng APIs trong một thời gian dài, có lẽ bạn đã chọn JSON mà không cần suy nghĩ nhiều. Nó là ngôn ngữ chung của web: dễ đọc, dễ tạo và được hỗ trợ ở khắp nơi.
Nhưng điều gì có thể sai? Thực ra, giống như một gói thử nghiệm "miễn phí" mà bạn quên hủy, JSON đi kèm với những chi phí tiềm ẩn mà chỉ xuất hiện sau này — thường là khi API của bạn chịu tải thực sự hoặc mô hình dữ liệu của bạn bắt đầu phát triển.
Một bài học lịch sử nhanh: XML → JSON → ???
Bạn có nhớ khi XML là mặc định cho APIs?
- Nhãn rườm rà khắp nơi
- Dấu ngoặc nhọn vô tận
- Phân tích đau đớn
JSON như một hơi thở mới — ngắn gọn hơn, sạch sẽ hơn và dễ đọc hơn. Nhưng có một điều: JSON cũng rườm rà. Có thể không tệ bằng XML, nhưng một khi tải trọng của bạn bắt đầu tăng lên, bạn sẽ nhận ra rằng nó vẫn mang theo nhiều trọng lượng không cần thiết.
Vậy là, chúng ta đã đổi một loại sự rườm rà này sang một loại khác.
1. Verbosity = Khối lượng lớn hơn
Dưới đây là một ví dụ đơn giản:
json
{
"id": 123,
"username": "john_doe",
"isActive": true
}
Nhìn có vẻ vô hại. Nhưng khi mô hình dữ liệu của bạn phát triển, mỗi tên trường sẽ được lặp lại, mỗi đối tượng lồng nhau sẽ được bọc trong dấu ngoặc. So với một định dạng nhị phân như Protobuf: cùng một dữ liệu có thể được biểu diễn bằng ít byte hơn — đôi khi nhỏ hơn 5 lần.
Tại quy mô lớn, sự rườm rà có nghĩa là:
- Chi phí băng thông cao hơn
- Chuyển dữ liệu chậm hơn
- Thời gian phân tích các tải trọng lớn nhiều hơn
2. Chi phí phân tích
Phân tích JSON tốn CPU nhiều hơn so với các định dạng nhị phân.
Một ví dụ cổ điển:
java
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class);
Nghe đơn giản, đúng không? Nhưng sau lớp vỏ bọc:
- Bộ phân tích quét từng ký tự.
- Xác thực cấu trúc.
- Ánh xạ về chuỗi, số, boolean.
- Sau đó xây dựng các đối tượng Java.
Trên một yêu cầu đơn lẻ, không có gì lớn. Trên 10k yêu cầu mỗi giây? Đột nhiên CPU của bạn phải làm việc quá sức chỉ để chuyển đổi văn bản thành các đối tượng.
3. Số là... Khó hiểu
JSON không phân biệt giữa các kích thước số nguyên.
123
có thể làint
,long
, hoặcBigInteger
.123.45
có thể làfloat
,double
, hoặcBigDecimal
.
Tôi đã từng gặp một lỗi sản xuất khi một trường JSON chứa ID đơn hàng (1234567890123456789
) lặng lẽ mất độ chính xác vì client đã đối xử nó như một số double.
Bài học rút ra: JSON yêu thích sự mơ hồ khi nói đến số.
4. Ngày & Giờ = Đau đầu
Câu hỏi nhanh: làm thế nào để bạn đại diện cho một ngày trong JSON?
- Chuỗi ISO-8601?
- Timestamp Unix?
- Chuỗi tùy chỉnh "MM/dd/yyyy"?
Mỗi hệ thống làm điều đó khác nhau.
Và phần tồi tệ nhất? JSON không quan tâm — nó không có kiểu ngày bản địa.
Tôi đã dành hàng giờ để gỡ lỗi các lỗi “chênh lệch một ngày” vì một dịch vụ tuần tự hóa ngày trong UTC, trong khi một dịch vụ khác lại ở múi giờ địa phương, và JSON không cho tôi bất kỳ gợi ý nào.
5. Thiếu sơ đồ = Bất ngờ
JSON không có sơ đồ, điều này cảm thấy thoải mái lúc đầu... cho đến khi ứng dụng client của bạn gặp sự cố vì ai đó đã đổi tên một trường.
Ví dụ:
json
{
"firstName": "John"
}
Trở thành:
json
{
"givenName": "John"
}
Đột nhiên một nửa client của bạn bị hỏng — và bạn thậm chí không biết cho đến khi các báo cáo lỗi ập đến. Với Protobuf hoặc Avro, các sơ đồ là rõ ràng và sự phát triển là an toàn hơn. Với JSON, bạn phải dựa vào “hãy cẩn thận, đừng thay đổi đồ vật.”
6. Rủi ro bảo mật ẩn
Tin tặc yêu thích JSON vì các bộ phân tích có mặt ở khắp nơi và các triển khai thì khác nhau. Một số chi phí ẩn:
- JSON bombs: các đối tượng lồng nhau sâu khiến các bộ phân tích bị sập.
- Tấn công tiêm: các giá trị không được thoát đúng cách.
- Tấn công tải trọng lớn: lạm dụng tính rườm rà để làm cạn kiệt bộ nhớ.
Bộ phân tích JSON “đơn giản” của bạn bỗng nhiên trở thành một bề mặt tấn công mới.
7. Mệt mỏi về công cụ
Bạn đã bao giờ cố gắng phiên bản các APIs JSON? Hoặc tạo ra các client an toàn với kiểu? Hoặc xác thực các tải trọng lớn?
Có thể — nhưng khá cồng kềnh. Bạn kết thúc việc phải gắn công cụ:
- JSON Schema
- Trình tạo mã
- Bộ xác thực thủ công
Trong khi đó, các định dạng nhị phân đi kèm với các sơ đồ tích hợp sẵn, và việc tạo mã “chỉ hoạt động.”
Sự chuyển mình trong ngành: Ngay cả LinkedIn cũng đã từ bỏ
Đây không chỉ là lý thuyết.
LinkedIn nổi tiếng đã chuyển từ JSON sang Protobuf cho các APIs nội bộ của họ. Tại sao?
- Giảm kích thước tải trọng
- Giảm chi phí CPU cho việc phân tích
- Đảm bảo sơ đồ mạnh mẽ
Khi một công ty hoạt động ở quy mô của LinkedIn thực hiện sự chuyển mình đó, đáng để bạn chú ý.
Vậy, chúng ta có nên từ bỏ JSON không?
Không hề. JSON vẫn tỏa sáng cho:
- Tính khả đọc của con người (gỡ lỗi thì dễ dàng).
- Lập nguyên mẫu (nhanh và bẩn thì có hiệu quả).
- Tính tương tác (mọi ngôn ngữ đều có thể sử dụng nó).
Nhưng đối với các APIs quy mô lớn, những chi phí ẩn của JSON tích tụ nhanh chóng.
Quy tắc cá nhân của tôi:
- Sử dụng JSON cho các APIs bên ngoài/công khai nơi tính tương tác quan trọng.
- Sử dụng Protobuf/Avro/MessagePack cho các APIs nội bộ nơi hiệu quả quan trọng.
Kết luận
JSON giống như pizza. 🍕
Ai cũng yêu thích nó, nó ở khắp nơi và thường thì nó hoạt động tốt.
Nhưng nếu bạn ăn pizza mỗi ngày, cuối cùng bạn sẽ hối tiếc.
Vì vậy, lần tới khi bạn thiết kế một API, hãy tự hỏi mình:
👉 Tôi thực sự cần JSON ở đây không, hay đã đến lúc tìm một cái gì đó nhanh hơn, an toàn hơn, và nhẹ nhàng hơn?