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