开始阅读以前,先了解一下概念。rpc, gprc, protocol buffer,bff前端
grpc调用过程如图1: node
简单来时就是为前端不一样的设备创建对应的后端。如图: 程序员
hello grpc
解决概念问题后,先来看看nodejs如何实现grpc server和client调用。如图1后端
hello.protoapi
syntax = "proto3"; // 语法proto3
package greeter; // 包名
/**
package greeter 包含两个service:Hello和 SelfIntro
message 定义了rpc方法参数和返回值的结构
*/
service Hello {
rpc SayHello (SayHelloRequest) returns (SayHelloResponse) {}
}
service SelfIntro {
rpc IntroMyself (SelfIntroRequest) returns (SelfIntroResponse) {}
}
message SayHelloRequest {
string name = 1;
}
message SayHelloResponse {
string message = 1;
}
message SelfIntroRequest {
}
message SelfIntroResponse {
string job = 1;
}
复制代码
grpc使用protobuf有两种方式。 一种是使用Protobuf.js在运行时动态生成代码,另外一种是使用protoc编译器生成静态的代码(生成对应的结构和方法)。 本篇实例采用前者。bash
const grpc = require("grpc")
const protoLoader = require("@grpc/proto-loader")
const packageDescripter = protoLoader.loadSync(
__dirname+ '/../hello.proto',
{
keepCase: true
}
)
const gretterPackage = grpc.loadPackageDefinition(packageDescripter).greeter
复制代码
// ... load proto
/** * 实现rpc方法 SayHello,IntroMyself * 在50051端口启动服务 */
function SayHello(call, callback) {
callback(null, {message: 'Hello ' + call.request.name})
}
function IntroMyself(call, callback) {
callback(null, {job: 'program enginner'})
}
function main() {
const server = new grpc.Server()
server.addService(gretterPackage.Hello.service, {
SayHello,
})
server.addService(gretterPackage.SelfIntro.service, {
IntroMyself,
})
server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure())
server.start(() => {
console.log('server runing on prot 50051')
})
}
复制代码
// ... load proto
/** * 建立服务端存根(stub) * 调用远程方法 */
function main () {
// 指定远端为localhost:50051
const stubHello = new gretterPackage.Hello('localhost:50051', grpc.credentials.createInsecure())
const name = "joe"
stubHello.SayHello({name}, (err, response) => {
if (err) { return console.error(err) }
console.log('Greeting: ', response.message)
})
const stubIntro = new gretterPackage.SelfIntro('localhost:50051', grpc.credentials.createInsecure())
stubIntro.IntroMyself({}, (err, response) => {
if (err) { return console.error(err) }
console.log('SelfIntro: my job is', response.job)
})
}
复制代码
到此为止咱们已经体验了一个完整的grpc调用过程。一般定义protobuf和server实现由后端完成。node这一层建立客户端,拿到rpc结果,以restful api的方式提供给前端restful
咱们经过proto知道远端定义那些service,有哪些prc方法,以及调用参数和返回值类型。因为js是弱类型的语言,在实际应用中,没有办法校验参数的合法性,也不能再写代码的时候提供补全提示等。为此,引入ts解决以上问题。框架
可是,grpc只是支持nodejs。这就须要经过某种方式安卓proto文件生成对应的ts文件ide
types.ts函数
// This file is auto generated by grpc-code-gen, do not edit!
// tslint:disable
export namespace greeter {
export interface SayHelloRequest {
'name'?: string;
}
export interface SayHelloResponse {
'message'?: string;
}
export interface SelfIntroRequest {
}
export interface SelfIntroResponse {
'job'?: string;
}
}
复制代码
greeter/Hello.ts
export interface IHello {
$FILE_NAME: string;
new (address: string, credentials: ChannelCredentials, options?: object): IHello;
/** @deprecated 请使用: SayHelloV2 */
SayHello(
request: types.greeter.SayHelloRequest,
options?: { timeout?: number; flags?: number; host?: string; }
): Promise<types.greeter.SayHelloResponse>;
/** @deprecated 请使用: SayHelloV2 */
SayHello(
request: types.greeter.SayHelloRequest,
metadata: MetadataMap,
options?: { timeout?: number; flags?: number; host?: string; }
): Promise<types.greeter.SayHelloResponse>;
SayHelloV2(option: {
request: types.greeter.SayHelloRequest;
metadata?: MetadataMap;
options?: { timeout?: number; flags?: number; host?: string; };
}): Promise<{ response:types.greeter.SayHelloResponse, metadata: Metadata }>;
}
export const hello: IHello = new greeter.Hello(`${serviceConfig.host}:${serviceConfig.port}`, credentials);
复制代码
clinet.ts
import {hello} from "greeter/Hello.ts"
import * as types from "types"
export sayHello = (req: types.greeter.SayHelloRequest): Promise<types.greeter.SayHelloResponse> => {
return hello.SayHello(req)
}
sayHello({name: 'grpc'})
复制代码