为什么选择 gRPC?
gRPC 提供二进制序列化(比 JSON 更快)、通过 Protocol Buffers 实现契约优先开发,以及对流式传输的原生支持——这些都是 REST 无法比拟的。

定义你的服务
// user.proto
syntax = "proto3";
package user;
service UserService {
// Unary RPC
rpc GetUser (GetUserRequest) returns (UserResponse);
// Server streaming
rpc ListUsers (ListUsersRequest) returns (stream UserResponse);
// Client streaming
rpc CreateUsers (stream CreateUserRequest) returns (CreateUsersResponse);
// Bidirectional streaming
rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}
message GetUserRequest {
string id = 1;
}
message UserResponse {
string id = 1;
string name = 2;
string email = 3;
int64 created_at = 4;
}
message ListUsersRequest {
int32 page = 1;
int32 page_size = 2;
string filter = 3;
}
服务器实现
import grpc from '@grpc/grpc-js';
import protoLoader from '@grpc/proto-loader';
import { fileURLToPath } from 'url';
import path from 'path';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const packageDef = protoLoader.loadSync(
path.join(__dirname, 'user.proto'),
{ keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }
);
const UserProto = grpc.loadPackageDefinition(packageDef).user;
const userService = {
getUser: async (call, callback) => {
const { id } = call.request;
try {
const user = await db.users.findById(id);
if (!user) {
return callback({
code: grpc.status.NOT_FOUND,
message: 'User not found',
});
}
callback(null, user);
} catch (err) {
callback({ code: grpc.status.INTERNAL, message: err.message });
}
},
listUsers: async (call) => {
const { page, page_size, filter } = call.request;
try {
const users = await db.users.findMany({ page, pageSize: page_size, filter });
for (const user of users) {
call.write(user); // Stream each user
}
call.end();
} catch (err) {
call.destroy(err);
}
},
};
const server = new grpc.Server();
server.addService(UserProto.UserService.service, userService);
server.bindAsync(
'0.0.0.0:50051',
grpc.ServerCredentials.createInsecure(),
(err, port) => {
if (err) throw err;
console.log('gRPC server running on port', port);
}
);
客户端实现
const client = new UserProto.UserService(
'localhost:50051',
grpc.credentials.createInsecure()
);
// Unary call
const getUser = (id) => new Promise((resolve, reject) => {
client.getUser({ id }, (err, response) => {
if (err) reject(err);
else resolve(response);
});
});
// Server streaming
function streamUsers(filter) {
const stream = client.listUsers({ page: 1, page_size: 100, filter });
return new Promise((resolve, reject) => {
const users = [];
stream.on('data', (user) => users.push(user));
stream.on('end', () => resolve(users));
stream.on('error', reject);
});
}
拦截器(中间件)
// Server interceptor for logging
function loggingInterceptor(options, nextCall) {
return new grpc.ServerInterceptingCall(nextCall(options), {
start: function(metadata, listener, next) {
const start = Date.now();
next(metadata, {
...listener,
onReceiveMessage: function(message, next) {
next(message);
},
onSendMessage: function(message, next) {
console.log(`RPC ${options.method_definition.path}: ${Date.now() - start}ms`);
next(message);
}
});
}
});
}
gRPC vs REST
| 特性 | gRPC | REST |
|---|---|---|
| 协议 | HTTP/2 | HTTP/1.1+ |
| 载荷 | 二进制(Protobuf) | JSON/XML |
| 流式传输 | 原生支持 | SSE/WebSocket |
| 类型安全 | 编译时 | 运行时 |
| 浏览器支持 | 有限 | 通用 |
| 性能 | 约快10倍 | 标准 |
生产环境中的 TLS
const credentials = grpc.credentials.createSsl(
fs.readFileSync('ca-cert.pem'),
fs.readFileSync('client-key.pem'),
fs.readFileSync('client-cert.pem')
);