Node.js使用google-protobuf

常永长
2023-12-01

    随着Google的Protobuf3的发布,Google终于开发了一个可以给JavaScript使用的库。之前大家如果在node端使用了Protobuf应该用的是protobufjs这个库,但是既然Google官方支持了JavaScript,那么我们还是要去尝试一下的。

    下面就开始介绍如何在node端使用google-protobuf,在解析生成proto数据的同时,我们还支持一个功能:可以根据客户端请求的后缀去判断返回给客户端的数据格式(json或protobuf)。举个例子,如果客户端请求的url是http://127.0.0.1:9999/user.json,我们给他返回的就是json数据;如果客户端请求的url是http://127.0.0.1:9999/user.pb,我们给他返回的就是proto数据。下面开始详细的讲解:


一、定义Protobuf的数据结构

    关于Protobuf3如何定义数据结构的,就不详细讲解了,贴上demo中的数据结构:

body.proto:主要用来定义请求的数据结构,其中引用了define.proto:

syntax = "proto3";

import "define.proto";

message MatchStockResp {
    ErrorCode errorCode = 1;
    repeated MatchStock stock = 2;
}

message MatchStock {
    string stockLabel = 1;
    string stockName = 2;
}

define.proto:主要用来定义一些常量

syntax = "proto3";

enum ErrorCode {
    DEFAULT = 0;
    SUCCESS = 1;
    FAIL = 2;
    FORM_INVALID = 3;
}


二、生成Protobuf对应的JS文件

    下载protoc.exe,用于编译.proto文件。编译代码如下:

protoc --js_out=import_style=commonjs,binary:. body.proto define.proto
编译成功后,会生成body_pb.js和define_pb.js两个文件,将其考入到Node的项目中,便于之后的引用。


三、Node.js端引用Protobuf生成的JS文件

    首先安装下google-proto的库(npm install --save google-protobuf)。接下来我们定义一个帮助类去引用我们生成的JS文件。

proto-helper.js:用于引用proto生成的js文件
/**
 * Created by wyk on 17/3/27.
 */

var bodyProto = require("../../proto/body_pb");
var defineProto = require("../../proto/define_pb");

module.exports = {
    bodyProto: bodyProto,
    defineProto: defineProto
};

定义完帮助类之后,我们就可以在需要生成Protobuf对象的地方去引用它了。具体的用法如下:
var bodyProto = require('../server/utils/proto-helper').bodyProto;
var defineProto = require('../server/utils/proto-helper').defineProto;

router.post('/matchStock.*', function (req, res, next) {
    var formMatchStr = req.body.matchStr;
    var formMatchCount = req.body.matchCount;

    var matchStockResp = new bodyProto.MatchStockResp();
    var matchStockList = stockUtils.matchStock(formMatchStr, formMatchCount);
    for (var i in matchStockList) {
        var matchStock = new bodyProto.MatchStock();
        matchStock.setStocklabel(matchStockList[i].label);
        matchStock.setStockname(matchStockList[i].name);
        matchStockResp.addStock(matchStock)
    }
    matchStockResp.setErrorcode(defineProto.ErrorCode.SUCCESS);

    res.send(commonUtils.responseByUrl(matchStockResp, req.url));
});
上面的代码中定义了一个处理post请求的方法,该方法使用了一个*的通配符,所以他可以接受任意后缀的请求,通过req.url可以拿到客户端请求的url,最后根据url的后缀去返回相应的数据结构。

最后再贴上根据请求url的后缀去判断返回数据结构的方法,也就是上面代码中的commonUtils.responseByUrl():
var responseByUrl = function (resp, url) {
    var lastIndex = url.lastIndexOf('.');
    if(lastIndex > -1) {
        var suffix = url.substr(lastIndex + 1);
        if (suffix == 'pb') {
            return resp.serializeBinary();
        } else {
            return resp.toObject();
        }
    } else {
        return resp.toObject();
    }
};
resp.serializeBinary()方法会将我们的对象序列化;resp.toObject()会将我们的proto对象转换为普通的对象。


2017.4.10添加

附上生成protojava文件的代码:

第一步需要在proto文件的头部(‘syntax = "proto3";’的下一行)添加生成proto的java文件的包名:package com.qn.gpcloud.proto;

第二步编写bat文件,编译proto文件:

protoc -I=. --java_out=. *.proto


 类似资料: