Hệ thống RPC mới Cap'n Web cho trình duyệt và máy chủ web
(blog.cloudflare.com)- Cap'n Web là giao thức RPC mới được triển khai bằng TypeScript, được tối ưu cho môi trường web và hoạt động trên nhiều runtime JavaScript khác nhau
- Không cần schema hay boilerplate rườm rà, cung cấp tuần tự hóa dựa trên JSON và định dạng dữ liệu con người có thể đọc được
- Thông qua mô hình dựa trên object-capability, nó hỗ trợ gọi hai chiều, truyền tham chiếu hàm và đối tượng, promise pipelining, và triển khai các mẫu bảo mật
- Hỗ trợ nhiều môi trường mạng như WebSocket, HTTP, postMessage, đồng thời là mã nguồn mở nhẹ dưới 10kB
- Không chỉ giải quyết vấn đề waterfall tương tự GraphQL, mà còn cho phép mô hình hóa RPC tự nhiên như API JavaScript thông thường
Cap'n Web là gì
- Cap'n Web là hệ thống RPC (protocol) mã nguồn mở dựa trên TypeScript do Cloudflare phát triển
- Lấy cảm hứng từ Cap'n Proto, nhưng hoạt động không cần định nghĩa schema riêng, và áp dụng cách tuần tự hóa thân thiện với con người bằng JSON
- Tích hợp với TypeScript để cải thiện trải nghiệm lập trình viên như tự động hoàn thành và kiểm tra kiểu; việc xác thực kiểu ở runtime có thể xử lý riêng (như type guard)
- Hỗ trợ các giao thức mạng như HTTP, WebSocket, postMessage và chạy trên các trình duyệt chính, Cloudflare Workers, Node.js
- Cấu trúc nhẹ không phụ thuộc thư viện, có kích thước dưới 10kB sau khi minify + gzip
Mô hình object-capability (OCap) của Cap'n Web
- Áp dụng mô hình dựa trên object-capability, cho phép biểu đạt phong phú hơn các hệ thống RPC hiện có
- Gọi hai chiều: client và server có thể gọi hàm của nhau
- Truyền tham chiếu hàm và đối tượng: khi truyền hàm hoặc đối tượng qua RPC, phía nhận sẽ nhận stub và khi gọi thì thực thi tại vị trí gốc
- Promise Pipelining: khi nối chuỗi nhiều RPC, có thể xử lý chỉ với một lần round trip mạng
- Mẫu bảo mật: có thể triển khai tự nhiên các cơ chế kiểm soát bảo mật như cấp quyền và quản lý phiên
Cách sử dụng cơ bản
-
Ví dụ phía client
import { newWebSocketRpcSession } from "capnweb" let api = newWebSocketRpcSession("wss://example.com/api") let result = await api.hello("World") console.log(result) -
Ví dụ phía server (dựa trên Cloudflare Worker)
import { RpcTarget, newWorkersRpcResponse } from "capnweb" class MyApiServer extends RpcTarget { hello(name) { return `Hello, ${name}!` } } export default { fetch(request, env, ctx) { let url = new URL(request.url) if (url.pathname === "/api") { return newWorkersRpcResponse(request, new MyApiServer()) } return new Response("Not found", {status: 404}) } } -
Có thể dễ dàng thêm method vào API, truyền callback từ client, cũng như định nghĩa và áp dụng interface TypeScript
RPC là gì và đặc điểm trong Cap'n Web
- RPC (Remote Procedure Call) là khái niệm cho phép hai chương trình trên mạng giao tiếp như thể đang gọi hàm
- Khác với giao thức HTTP/REST truyền thống, RPC cung cấp lớp trừu tượng gọi hàm, cho phép viết mã phù hợp với cách tư duy của lập trình viên
- Cap'n Web phù hợp với luồng JavaScript hiện đại nhờ hỗ trợ async/await, Promise, Exception
- Không giống những tranh cãi lịch sử về RPC (gọi đồng bộ, lỗi mạng), trong môi trường JS hiện đại nó có thể được dùng an toàn và hiệu quả hơn
Các kịch bản sử dụng Cap'n Web
- Phù hợp với mọi môi trường cần giao tiếp mạng giữa hai ứng dụng JavaScript
- Gọi giữa client-server, giữa các microservice, v.v.
- Đặc biệt phù hợp với web app cộng tác thời gian thực và các tương tác vượt qua ranh giới bảo mật phức tạp
- Vẫn đang ở giai đoạn thử nghiệm, nên càng hữu ích hơn với các nhà phát triển cởi mở với công nghệ mới
Nhiều tính năng khác nhau
Chế độ batch HTTP
-
Khi không cần kết nối liên tục, có thể dùng chế độ batch HTTP để gộp nhiều lệnh gọi RPC và xử lý cùng lúc
import { newHttpBatchRpcSession } from "capnweb" let batch = newHttpBatchRpcSession("https://example.com/api") let result = await batch.hello("World") console.log(result) -
Có thể thực thi đồng thời nhiều lời gọi trong cùng một batch và nhận kết quả song song
let promise1 = batch.hello("Alice") let promise2 = batch.hello("Bob") let [result1, result2] = await Promise.all([promise1, promise2])
Promise Pipelining (gọi chuỗi)
-
Hỗ trợ cách dùng ngay kết quả làm đối số cho lệnh gọi tiếp theo mà không cần chờ lệnh gọi trước hoàn tất
-
Ví dụ: truyền trực tiếp Promise kết quả của
getMyName()vàohello()để xử lý chỉ với một lần round trip mạnglet namePromise = batch.getMyName() let result = await batch.hello(namePromise) -
Promise của Cap'n Web hoạt động như đối tượng proxy, cho phép nối thêm method mà không bị trì hoãn
let sessionPromise = batch.authenticate(apiKey) let name = await sessionPromise.whoami()
Bảo mật: xác thực và object-capability
- Thông qua method authenticate, hệ thống cấp đối tượng quyền hạn (session) khi thành công, sau đó có thể gọi chức năng mà không cần thêm bước xác thực
- Khác với RPC truyền thống, không thể giả mạo đối tượng session, và cũng không thể truy cập method yêu cầu quyền nếu chưa xác thực
- Tự nhiên khắc phục các giới hạn cấu trúc của WebSocket, đồng thời bảo đảm tính nhất quán của logic xác thực
- Khi khai báo interface API bằng TypeScript, có thể tự động áp dụng từ client đến server, đồng thời bảo đảm tự động hoàn thành và độ an toàn kiểu
So sánh với GraphQL và điểm khác biệt của Cap'n Web
-
GraphQL làm giảm vấn đề waterfall nhiều tầng của REST, nhưng đòi hỏi đưa vào ngôn ngữ, schema và toolchain mới
-
Cap'n Web giải quyết vấn đề waterfall chỉ bằng mã JavaScript, đồng thời
- nhờ hỗ trợ promise pipelining/tham chiếu đối tượng, có thể mô hình hóa tự nhiên các lời gọi lồng nhau hoặc logic giao dịch phức hợp
let user = api.createUser({ name: "Alice" }) let friendRequest = await user.sendFriendRequest("Bob") -
Có thể sử dụng giống API JavaScript mà không cần gánh độ phức tạp và chi phí học tập/quản lý của GraphQL
Phép toán mảng (array.map v.v.) và tối ưu hóa
-
Trong Cap'n Web, có thể thực hiện phép
maptrên từng phần tử mảng mà không phát sinh thêm round trip mạng -
Hàm callback của
mapđược chạy một lần ở client để ghi lại nội dung tính toán (record-replay), sau đó gửi lên server để xử lý hàng loạt phía serverlet friendsWithPhotos = friendsPromise.map(friend => { return {friend, photo: api.getUserPhoto(friend.id)} }) let results = await friendsWithPhotos -
Thông qua một DSL chuyên biệt trong phạm vi giới hạn, nó cho phép biểu đạt như hàm JavaScript, nhưng thực tế dùng giao thức Cap'n Web để tối ưu nhiều lời gọi
Cấu trúc giao thức nội bộ và luồng giao tiếp
- Truyền dữ liệu có cấu trúc thông qua JSON + tiền xử lý đặc biệt, hỗ trợ các kiểu đặc biệt như mảng, ngày tháng
- Giao thức đối xứng cho phép giao tiếp hai chiều không phân biệt client và server
- Mỗi bên (ví dụ: Alice và Bob) quản lý bảng export/import, và phân biệt tham chiếu đối tượng/hàm bằng ID
- Thông qua các thông điệp push/pull và việc cấp phát Promise ID, có thể phản ánh nhiều lời gọi trong một round trip
Tình hình hiện tại và các trường hợp áp dụng
- Cap'n Web hiện vẫn là mã nguồn mở mang tính thử nghiệm, nhưng đã được dùng trong dịch vụ thực tế như remote bindings của Cloudflare Wrangler
- Dự kiến sẽ có thêm các bài blog và nhiều thử nghiệm frontend khác
- Được phát hành theo giấy phép MIT, bất kỳ ai cũng có thể tự do áp dụng
- Đi tới kho GitHub
Chưa có bình luận nào.