electron + grpc 项目中grpc 启动出错

逍遥峰
  • 33

项目中用的electron 13.1.7, @grpc/grpc-js 1.3.6

一开始在外面独立试了试grpc的demo, 用的还是grpc, 而不是@grpc/grpc-js

放到electron,重新npm install 之后,报了错,才改为@grpc的。

grpc的serve端文件中,把bind函数替换成bindAsync函数,出现如下报错

App threw an error during load
TypeError: callback must be a function
    at Server.bindAsync (C:\Users\zhijue\project\electron\node_modules\@grpc\grpc-js\build\src\server.js:147:19)
    at main (C:\Users\zhijue\project\electron\greeter_server.js:56:10)
    at Object.<anonymous> (C:\Users\zhijue\project\electron\greeter_server.js:61:1)
    at Module._compile (internal/modules/cjs/loader.js:1078:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1108:10)
    at Module.load (internal/modules/cjs/loader.js:935:32)
    at Module._load (internal/modules/cjs/loader.js:776:14)
    at Function.f._load (electron/js2c/asar_bundle.js:5:12913)
    at Module.require (internal/modules/cjs/loader.js:959:19)
    at require (internal/modules/cjs/helpers.js:88:18)

去看了源码文件,发现这个新函数要多传一个回调

 bind(port, creds) { // 原来的bind
        throw new Error('Not implemented. Use ```bindAsync() instead');
    }
 bindAsync(port, creds, callback) {  // 现在的函数

但是对于这个回调函数,我没太看懂,源码里面是这么调用callback的

        const resolverListener = {
            onSuccessfulResolution: (addressList, serviceConfig, serviceConfigError) => {
                // We only want one resolution result. Discard all future results
                resolverListener.onSuccessfulResolution = () => { };
                if (addressList.length === 0) {
                    callback(new Error(`No addresses resolved for port ${port}`), 0);
                    return;
                }
                let bindResultPromise;
                if (subchannel_1.isTcpSubchannelAddress(addressList[0])) {
                    if (addressList[0].port === 0) {
                        bindResultPromise = bindWildcardPort(addressList);
                    }
                    else {
                        bindResultPromise = bindSpecificPort(addressList, addressList[0].port, 0);
                    }
                }
                else {
                    // Use an arbitrary non-zero port for non-TCP addresses
                    bindResultPromise = bindSpecificPort(addressList, 1, 0);
                }
                bindResultPromise.then((bindResult) => {
                    if (bindResult.count === 0) {
                        const errorString = `No address added out of total ${addressList.length} resolved`;
                        logging.log(constants_1.LogVerbosity.ERROR, errorString);
                        callback(new Error(errorString), 0);
                    }
                    else {
                        if (bindResult.count < addressList.length) {
                            logging.log(constants_1.LogVerbosity.INFO, `WARNING Only ${bindResult.count} addresses added out of total ${addressList.length} resolved`);
                        }
                        callback(null, bindResult.port);
                    }
                }, (error) => {
                    const errorString = `No address added out of total ${addressList.length} resolved`;
                    logging.log(constants_1.LogVerbosity.ERROR, errorString);
                    callback(new Error(errorString), 0);
                });
            },
            onError: (error) => {
                callback(new Error(error.details), 0);
            },
        };

然后在服务端代码中,我传了一个空函数进去

function main() {
  const server = new grpc.Server();
  server.addService(hello_proto.Greeter.service, {
    SayHello: sayHello2,
    printAge: printAge2
  });
  server.bindAsync('127.0.0.1:50051', grpc.ServerCredentials.createInsecure(),function(){});
  server.start();
  console.log('server start......')
}

main()

出现如下报错

App threw an error during load
Error: server must be bound in order to start
    at Server.start (C:\Users\zhijue\project\electron\node_modules\@grpc\grpc-js\build\src\server.js:336:19)
    at main (C:\Users\zhijue\project\electron\greeter_server.js:57:10)
    at Object.<anonymous> (C:\Users\zhijue\project\electron\greeter_server.js:61:1)
    at Module._compile (internal/modules/cjs/loader.js:1078:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1108:10)
    at Module.load (internal/modules/cjs/loader.js:935:32)
    at Module._load (internal/modules/cjs/loader.js:776:14)
    at Function.f._load (electron/js2c/asar_bundle.js:5:12913)
    at Module.require (internal/modules/cjs/loader.js:959:19)
    at require (internal/modules/cjs/helpers.js:88:18)
No address added out of total 1 resolved

下面是我对源码的一些分析:
首先是在bindAsybc函数中,有一个bindSpectficPort的函数中,会把一个http2服务加入http2ServerList数组中,然后在start()函数中,会对http2ServerList数组进行判断,如果为空数组,就会抛出错误。但是通过console.log发现,在进入了bindSpectficPort函数中后,并没有执行http2ServerList数组添加的操作,然后执行start函数了,所以一定会报错的

不知道有没有哪位大佬能看一看

下面为bindSpectficPorth函数源码

        const bindSpecificPort = (addressList, portNum, previousCount) => {
            if (addressList.length === 0) {
                return Promise.resolve({ port: portNum, count: previousCount });
            }
            return Promise.all(addressList.map((address) => {
                trace('Attempting to bind ' + subchannel_1.subchannelAddressToString(address));
                let addr;
                if (subchannel_1.isTcpSubchannelAddress(address)) {
                    addr = {
                        host: address.host,
                        port: portNum,
                    };
                }
                else {
                    addr = address;
                }
                const http2Server = setupServer();
                return new Promise((resolve, reject) => {
                    function onError(err) {
                        resolve(err);
                    }
                    http2Server.once('error', onError);
                    http2Server.listen(addr, () => {
                        trace('Successfully bound ' + subchannel_1.subchannelAddressToString(address));
                        console.log('push1')
                        this.http2ServerList.push(http2Server);
                        const boundAddress = http2Server.address();
                        if (typeof boundAddress === 'string') {
                            resolve(portNum);
                        }
                        else {
                            resolve(boundAddress.port);
                        }
                        http2Server.removeListener('error', onError);
                    });
                });
            })).then((results) => {
                let count = 0;
                for (const result of results) {
                    if (typeof result === 'number') {
                        count += 1;
                        if (result !== portNum) {
                            throw new Error('Invalid state: multiple port numbers added from single address');
                        }
                    }
                }
                return {
                    port: portNum,
                    count: count + previousCount,
                };
            });
        };
回复
阅读 382
1 个回答

第二天一早,我在知乎上发现了一个demo,终于发现了问题所在,在原来的bind函数的写法中,start()函数放在函数下面,但是在bindAsync中,start()应该放在回调函数中,这样就不会出现上述的问题。

 server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
        server.start()
        console.log('grpc server started')        
    })

参考文章链接 在Node.js中使用grpc-知乎

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
你知道吗?

宣传栏