gRPC除了1:1的请求应答模式之外,还支持流式的请求响应模式,而在JS语法下的使用语句也有点意思,这里做一个简单整理。
首先给出使用的proto定义文件,下文所有的代码都以这个定义为准:
请求模式:单请求,单响应
服务端:
request: RequestType
。node范式的回调函数
,第一位参数是error,第二位才是返回值。客户端:
Proto定义的请求:
rpc GetBook (GetBookRequest) returns (Book) {}
Server:
const getBook = (call: grpc.ServerUnaryCall<GetBookRequest>, callback: grpc.sendUnaryData<Book>) => {
const book = new Book();
book.setTitle("DefaultBook");
book.setAuthor("DefaultAuthor");
log(`[getBook] Done: ${JSON.stringify(book.toObject())}`);
callback(null, book);
};
client:
const getBook = async (isbn: number) => {
return new Promise((resolve, reject) => {
const request = new GetBookRequest();
request.setIsbn(isbn);
log(`[getBook] Request: ${JSON.stringify(request.toObject())}`);
client.getBook(request, (err, book: Book) => {
if (err != null) {
debug(`[getBook] err:\nerr.message: ${err.message}\nerr.stack:\n${err.stack}`);
reject(err); return;
}
log(`[getBook] Book: ${JSON.stringify(book.toObject())}`);
resolve(book);
});
});
};
请求模式:单请求,流式响应(多响应)
服务端:
request: RequestType
。write
返回ResponseType
,全部结束之后需要主动关闭可写入流,即end
。客户端:
data
事件,得到返回的ResponseType
数据,并监听end
事件,标识响应流的结束。Proto定义的请求:
rpc GetBooksViaAuthor (GetBookViaAuthor) returns (stream Book) {}
Server:
const getBooksViaAuthor = (call: grpc.ServerWriteableStream<GetBookViaAuthor>) => {
const request = call.request as GetBookViaAuthor;
log(`[getBooksViaAuthor] Request: ${JSON.stringify(request.toObject())}`);
for (let i = 1; i <= 10; i++) {
const reply = new Book();
reply.setTitle(`Book${i}`);
reply.setAuthor(request.getAuthor());
reply.setIsbn(i);
log(`[getBooksViaAuthor] Write: ${JSON.stringify(reply.toObject())}`);
call.write(reply);
}
log("[getBooksViaAuthor] Done.");
call.end();
};
Client:
const getBooksViaAuthor = (author: string) => {
return new Promise((resolve) => {
const request = new GetBookViaAuthor();
request.setAuthor(author);
log(`[getBooksViaAuthor] Request: ${JSON.stringify(request.toObject())}`);
const stream: grpc.ClientReadableStream<GetBookViaAuthor> = client.getBooksViaAuthor(request);
stream.on("data", (data: Book) => {
log(`[getBooksViaAuthor] Book: ${JSON.stringify(data.toObject())}`);
});
stream.on("end", () => {
log("[getBooksViaAuthor] Done.");
resolve();
});
});
};
请求模式:流式请求(多请求),单响应
服务端:
data
事件获得RequestType
,并监听end
事件得到所有请求获得结束的通知,然后开始处理服务端业务。客户端:
write
方法,向服务端发出RequestType
的请求,并使用end
方法通知服务端请求发送结束。ResponseType
结果。Proto定义的请求:
rpc GetGreatestBook (stream GetBookRequest) returns (Book) {}
Server:
const getGreatestBook = (call: grpc.ServerReadableStream<GetBookRequest>, callback: grpc.sendUnaryData<Book>) => {
let lastOne: GetBookRequest;
call.on("data", (request: GetBookRequest) => {
log(`[getGreatestBook] Request: ${JSON.stringify(request.toObject())}`);
lastOne = request;
});
call.on("end", () => {
const reply = new Book();
reply.setIsbn(lastOne.getIsbn());
reply.setTitle("LastOne");
reply.setAuthor("LastOne");
log(`[getGreatestBook] Done: ${JSON.stringify(reply.toObject())}`);
callback(null, reply);
});
};
Client:
const getGreatestBook = () => {
return new Promise((resolve) => {
const stream: grpc.ClientWritableStream<GetBookRequest> = client.getGreatestBook((err, data: Book) => {
if (err != null) {
log(`[getGreatestBook] err:\nerr.message: ${err.message}\nerr.stack:\n${err.stack}`);
}
log(`[getGreatestBook] Book: ${JSON.stringify(data.toObject())}`);
resolve();
});
for (let i = 0; i < 10; i++) {
const req = new GetBookRequest();
req.setIsbn(i);
log(`[getGreatestBook] Request: ${JSON.stringify(req.toObject())}`);
stream.write(req);
}
stream.end();
});
};
请求模式:流式请求(多请求),流式响应(多响应),实质上的请求和响应还是一对一关系
服务端:
data
事件,获取RequestType
的请求,并使用write
方法,返回ResponseType
的结果,这里基本上是1:1的应答关系。end
事件并处理完服务端逻辑之后,使用end
方法,告知客户端处理完成。客户端:
end
事件得知处理完成通知。Proto定义的请求:
rpc GetBooks (stream GetBookRequest) returns (stream Book) {}
Server:
const getBooks = (call: grpc.ServerDuplexStream<GetBookRequest, Book>) => {
call.on("data", (request: GetBookRequest) => {
const reply = new Book();
reply.setTitle(`Book${request.getIsbn()}`);
reply.setAuthor(`Author${request.getIsbn()}`);
reply.setIsbn(request.getIsbn());
log(`[getBooks] Write: ${JSON.stringify(reply.toObject())}`);
call.write(reply);
});
call.on("end", () => {
log("[getBooks] Done.");
call.end();
});
};
Client:
const getBooks = () => {
return new Promise((resolve) => {
const stream: grpc.ClientDuplexStream<GetBookRequest, Book> = client.getBooks();
stream.on("data", (data: Book) => {
log(`[getBooks] Book: ${JSON.stringify(data.toObject())}`);
});
stream.on("end", () => {
log("[getBooks] Done.");
resolve();
});
for (let i = 0; i < 10; i++) {
const req = new GetBookRequest();
req.setIsbn(i);
log(`[getBooks] Request: ${JSON.stringify(req.toObject())}`);
stream.write(req);
}
stream.end();
});
};
完整的代码请参见:v2.2.2/examples/src
EOF