javascript下grpc使用

2025-03-09 14:51:00

protobuf介绍

//notice.proto
syntax="proto3";//使用proto3语法
package notice;//类似命名空间,一个proto文件只允许一个package
//message定义结构体
message Request{
	required string accout=1;//required表示必须有值,'='表示第几个参数
	string password=2;
	repeated int32 array=3;//repeated类似数组定义,表示int32[]
	optional int32 code;//optional 表示参数可以为空
}
message Response{
	string name=1;
	int32 age=2;
	repeated string accounts=3;
}

message RequestFile{
	string filename=1;
}
message ResponseFile{
	string content=1;
}
//标准类型int32, int64, float, double, bool, string, bytes
//enum类型
enum Option{
	VALUE1=0;
	VALUE2=1;
}


//notice2.proto
import "./notice.proto"//导入其他proto文件

//服务类型
service Greeter{
	rpc sayHello (Request) returns (Response);
	//js中函数声明是function sayHello(call,callback);
	//call.request是上述Request结构
	//callback声明为(err,response)=>{};response是上述声明Response结构
}

流式rpc

客户机流式(发多个收一个)

rpc sayHelloStream (stream RequestFile) returns (ResponseFile);
//stream代表客户机发送多个Request
//上述js服务机代码片段(服务机返回数据不是流式)
function sayHelloStream(call,callback){
    let data=[];
    call.on('data',(request)=>{
        data.push(request.filename);
    });
    call.on('end',()=>{
        callback(null,{key:'其他属性'});
        console.log('消息已回传');
    });
    call.on('error',(err)=>{});
}
//######################################
//客户机片段(客户机发送数据是流式)
let call=client.sayHelloStream((err,response)=>{});

call.write({'Request结构体'});
call.write({'Request结构体'});//可以多次write,流式发送
call.end();

服务机流式(发一个收多个)

rpc sayHelloStream (RequestFile) returns (stream ResponseFile);
//上述js服务机代码片段(服务机返回数据是流式)
function sayHelloStream(call){
    //...一段检测要什么文件
    const readStream = fs.createReadStream(call.request.filename, 'utf8');
    readStream.on('data',(data)=>{
        call.write({content:data});//这里假设了一个Response
    });
    readStream.on('end',()=>{
       	console.log('读完了');
        call.end();//write方写完会启动接收方的end事件
    });
    readStream.on('error',(err)=>{
       console.log('错了');
    });
}
//######################################
//客户机片段(客户机接收数据是流式)
let call=client.sayHelloStream({filename:'xxx文件'});
call.on('data',(response)=>{
    fs.appendFileSync('xxx文件',response.content,'utf-8');
});
call.on('end',()=>{
    //读完了
});
call.on('error',(err)=>{//错了
});

双流式(多发多收)

rpc sayHelloStream (RequestFile) returns (stream ResponseFile);
//上述js服务机代码片段(服务机返回数据是流式)
function sayHelloStream(call){
    call.on('data',(request)=>{
        const readStream = fs.createReadStream(call.request.filename, 'utf8');
        readStream.on('data',(data)=>{
        	call.write({content:data});
    	});
        readStream.on('end',()=>{
       		console.log('读完了');
            call.end();
    	});
        readStream.on('error',(err)=>{
           console.log('错了');
        });
    });
    call.on('end',()=>{
        call.end();
    });
    call.on('error',(err)=>{
        
    });
}
//######################################
//客户机片段(客户机接收数据是流式)
let call=client.sayHelloStream();
call.on('data',(response)=>{
    fs.appendFileSync('xxx文件',response.content,'utf-8');
});
call.on('end',()=>{
    //读完了
});
call.on('error',(err)=>{//错了
});
call.write({filename:'xxx'});
call.write({filename:'xxx2'});
call.end();

Js grpc的使用

解析proto文件

//导入模块@grpc/grpc-js、@grpc/proto-loader
const grpc=require('@grpc/grpc-js');
const protoloader=require("@grpc/proto-loader");

//导入文件
const packageDefinition = protoloader.loadSync('文件.proto',{
    keepCase:true,//保持.proto中字段名原样,否则用驼峰式
    longs: String,//如何处理int64或uint64
    enums: String,//如何处理枚举类型
    defaults: true,//是否给字段默认值
    oneofs: true,
    /*
    * message xxx{
    * oneofs xxx2{
    *	int32 a=1;
    * }
    * }
    * true 解析为{xxx2:{a:0}}
    * false 解析为{a:0}
    */
    
    includeDirs:['路径1','路径2'],//指定proto文件搜索路径
    array: true,//true给repeated字段强制生产数组(只有一个值也是数组)
    json:true//将message解析为json格式
});

//导入package
const package=grpc.loadPackageDefinition(packageDefinition).包名;
//#######################################################################
//实现函数,上节介绍过流式实现
function SayHello(call,callback){
    
}
//服务定义server.js
let server=new grpc.Server();
server.addService(package.服务名.service,{
    //函数实现,比如sayHello:sayHello
});//服务名就是proto中有service关键字定义的东西

//启动服务
server.bindAsync(
    "0.0.0.0:50051",//服务端监听所有网络接口
    grpc.ServerCredentials.createInsecure(),//安全验证
    ()=>{
        console.log("服务启动")
    }//服务启动后回调
);
//#####################################################################
//客户机直接调用
let client=new package.服务名(
    '服务机IP:端口',
    grpc.credentials.createInsecure()//安全验证
);
client.sayHello({'request结构'},(err,reaponse)=>{});//本地调用远程接口

以上就是基本使用

目录