Đây có lẽ là câu hỏi mình nhận được nhiều nhất về Golang nói riêng và backend nói chung. Một số anh chị em đã triển khai gRPC nhưng sau đó thì tá hoả quay về REST với trạng thái ức chế vì cái công nghệ gì mà khó xài và dễ bị "nghẽn request" như thế. Tại sao lại như thế???
Từ khoá "REST vs gRPC" trên Google có quá nhiều bài viết rồi nên mình chỉ nói một cách đơn giản và dễ hiểu nhất. Các điểm khác biệt lớn nhất mà các anh em cần để ý:
1. REST là STATELESS, mỗi một request là một lần thiết lập connection TCP vơi backend (protocol http/1). gRPC thì thiết lập connection một lần rồi KEEP ALIVE sử dụng cho toàn bộ các trao đổi dữ liệu (http/2).
2. REST sử dụng ngôn ngữ trung gian (phổ biến nhất là JSON) để client và backend trao đổi thông tin, như vậy là cần encode/decode ở cả 2 đầu. gRPC dùng protobuf, cũng encode/decode NHƯNG theo structure riêng. Vì thế client và backend phải đều biết structure này (cùng import cái file pb gen từ protobuf ra).
3. REST request rồi đợi response rồi thôi, kết thúc vòng đời ở đó. gRPC vì vẫn còn connection nên có hỗ trợ cơ chế streaming. Đại khái thay vì gom 1 lần về sỉ thì có thể stream từng cái lẻ về, client nhận cái nào thì xử cái đó realtime. Client cũng có thể stream lên server (streaming 2 chiều).
4. REST sử dụng từng URI (và request method) để gọi đến backend. Nhưng gRPC bản chất là RPC nên cần interface description language (IDL), chỗ này xem ở protobuf file hoặc gen ra code rồi coi cũng được. Cái URL thiết lập gRPC connection thường chỉ dừng tới PORT. Tất cả các giao tiếp RPC sẽ thông qua interface từ file code gen.
5. Và cuối cùng, RPC = Remote Procedure Call thực sự chỉ là RPC... Tức là các bạn dùng nó để gọi một function/procedure thuẩn tuý hơn là nhiệm vụ của REST API, CRUD resources. VD hàm "add(int a, int b) int" có thể thực hiện ở 1 server khác nếu bạn thích RPC.
Từ các điểm khác biệt to lớn trên
1. gRPC nên dùng để giao tiếp backend to backend. CPU không gánh nhiều cost cho encode/decoding mỗi đầu nữa. Tuy nhiên đặc tính mỗi đầu cần import file model chung nên nếu update thì phải update đủ. Việc này vô tình tạo dependency nên nhiều ae không thích. gRPC thường được đấu nối vào service mesh, không nên connect trực tiếp vì không ai muốn đi maintain cái connection http/2 bằng tay.
2. gRPC support streaming 2 đầu nên rất được lòng các fan streaming system, event source (stream event). VD: gRPC được sử dụng trong: vitess, neo4j vì lý do trên.
3. gRPC nếu dùng cho frontend-backend thì thật sự rất cân nhắc. Connection statefull tạo nhiều sự khó chịu trong scale tải. Nếu bạn không nắm thì rất dễ bị thắt cổ chai. Chẳng thà như REST, cứ request là connection mới, load balancing hiệu quả hơn.
4. gRPC vẫn có thư viện gRPC Gateway chính chủ của Google. Tức là các bạn vẫn có thể chạy 1 port http/1 cho REST và 1 port http/2 của gRPC đồng thời. Như vậy không phải là không có cách để quay về REST thân quen, nhưng đương nhiên là đi qua proxy service (gateway rest) nên cồng kềnh hơn.
5. Vụ nghẽn request có rất nhiều nguyên nhân. Đa số bị khi dùng streaming nếu xử lý không đúng kỹ thuật thì nó lock lại ngay. Này là cơ chế giống với channel Golang, phía sender stream xuống thì receiver phải ở ngay đó mà lấy data ra. Như vậy 2 đầu chạy bất đồng bộ thì sẽ sync lại khi trao đổi dữ liệu (streaming). Giả sử nếu vì lý do nào đó một trong 2 đầu bị "stuck" hoặc leak cái connection là... đi bụi.
OK một vài so sánh nho nhỏ cho anh em thôi chứ còn nhiều khác biệt to lớn nữa. Chúc anh em chinh phục gRPC thành công nhé.
Bổ sung quan trọng: Do gRPC dùng http/2, trong một lúc có nhiều request đến đồng thời trong 1 connection. Nếu có bất kỳ connection nào bị thất lạc gói tin (một phần nhỏ của toàn bộ message) thì toàn bộ những gì theo sau request đó sẽ bị dừng lại cho tới khi gói tin đó tìm đường về. Đây được biết đến với tên: TCP head of line blocking.
(Fb Viet Tran)