Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to instantiate Service? #507

Closed
pronebird opened this issue Nov 30, 2016 · 13 comments
Closed

How to instantiate Service? #507

pronebird opened this issue Nov 30, 2016 · 13 comments
Labels

Comments

@pronebird
Copy link

pronebird commented Nov 30, 2016

Hi,

I am following the example at https://github.com/dcodeIO/protobuf.js/wiki/Services and creating my RPC.

Everything seems fine but I cannot create a new instance of Service. it says:

Service is not a constructor

  var Service = root.lookup('MailService');
  var RequestType = root.lookup('RequestType');
  var ResponseType = root.lookup('ResponseType');

  function rpcHandler(method, req, callback) {
    var request = RequestType.decode(data);
    var response = new ResponseType();

    if(method === '.MailService.addMailbox') {
      // add mailbox
    } else if(method === '.MailService.removeMailbox') {
     // remove mailbox
    }

    callback(response.toBuffer());
  }

  var srv = new Service(rpcHandler); // CRASH
@dcodeIO
Copy link
Member

dcodeIO commented Nov 30, 2016

The wiki refers to protobuf.js 5.

Using protobuf.js 6, services are instances of the service class here. You can look them up, but there is not a lot of functionality within. You can, however, get the resolved request and response types of the respective methods.

var service = root.lookup("MyService");
var method = service.methods["myMethod"].resolve();

var reqType = method.resolvedRequestType;
var resType = method.resolvedResponseType;
...

There has been a bit of discussion previously to support use cases like grpc out of the box, but as I am not using services myself, I'd require some suggestions.

@dcodeIO
Copy link
Member

dcodeIO commented Nov 30, 2016

This commit adds the following API:

var MyService = root.lookup("MyService");

// First, implement the RPC channel:
function rpc(method, requestData, callback) {
  // send requestData (binary) over the network
  ...
  // then call the callback with error, if any, and the received responseData (binary)
  callback(null, responseData);
}

// Create a runtime service:
var myService = MyService.create(/* your rpc implementation */ rpc, /* requestDelimited? */ true, /* responseDelimited? */ true);

// Call its methods (uses your RPC channel)
myService.MyMethod({ awesomeField: "awesomeText" }, function(err, responseMessage) {
   ...
});

@dcodeIO
Copy link
Member

dcodeIO commented Dec 1, 2016

Feel free to reopen if there are still any issues or if you have a suggestion on how this should be implemented.

@dcodeIO dcodeIO closed this as completed Dec 1, 2016
@pronebird
Copy link
Author

pronebird commented Dec 1, 2016

@dcodeIO thanks Daniel! Works like a charm!

One quick question, maybe I found a bug...:

So in RPC I do this:

function rpc(method, requestData, callback) {
  var RequestType = root.lookup('RequestType');

  // CRASH in ReaderPrototype.skip on throw indexOutOfRange(this);
  var request = RequestType.decode(requestData); 

Trying to debug:

$ requestData.toString()
"\u0000"

I know it's silly that my messages are empty at the moment, but I guess it shouldn't crash anyway and it's good for QA:

message RequestType {}
message ResponseType {}

service MailService {
  rpc addMailbox(RequestType) returns (ResponseType);
  rpc removeMailbox(RequestType) returns (ResponseType);
}

@dcodeIO
Copy link
Member

dcodeIO commented Dec 1, 2016

So, your messages are length delimited, right? Did you specify that to Service.create?

var myService = MyService.create(/* your rpc implementation */ rpc, /* requestDelimited? */ true, /* responseDelimited? */ true);

Otherwise the length-bytes will be interpreted as message contents and throwing there is actually what it should do.

@pronebird
Copy link
Author

pronebird commented Dec 1, 2016

Well I am not sure whether message without any fields is delimited or not. But if I create RPC with MailService.create(rpc, false, false) then it works fine. It feels like if there are no any fields in message then it shouldn't attempt to read it..

@dcodeIO
Copy link
Member

dcodeIO commented Dec 1, 2016

Above you mentioned that, in one of your messages, there was a single zero byte:

$ requestData.toString()
"\u0000"

That's why I assumed that it is a length-delimited empty message which of course throws if interpreted as a non-delimited message.

@pronebird
Copy link
Author

pronebird commented Dec 1, 2016

Yeah strange, that's how I call the method:

service.addMailbox(RequestType.create(), function (err, res) {
    console.log(err);
});

Not sure where 0-byte comes from. RequestType as is without any fields.

Update: if I specify that request is length delimited then rpc receives a buffer with one 0-byte, otherwise 0-length buffer is received.

@dcodeIO
Copy link
Member

dcodeIO commented Dec 1, 2016

Strange. in fact. The code encoding to a buffer is here: https://github.com/dcodeIO/protobuf.js/blob/master/src/service.js#L159

If shouldn't generate the zero byte, except if there is something true-ish specified for requestDelimited.

@pronebird
Copy link
Author

This is the source code if it helps ( bare bones)

#!/usr/bin/env node

var ProtoBuf = require("protobufjs");

ProtoBuf.load('mailservice.proto', function (err, root) {
  var MailService = root.lookup('MailService');
  var RequestType = root.lookup('RequestType');
  var ResponseType = root.lookup('ResponseType');

  function rpc(method, requestData, callback) {
    var request = RequestType.decode(requestData);

    if(method.fullName === '.MailService.addMailbox') {
      // add mailbox
      var response = ResponseType.create();
      callback(null, response);
    } else if(method.fullName === '.MailService.removeMailbox') {
      // remove mailbox
      var response = ResponseType.create();
      callback(null, response);
    } else {
      throw new Error('Not implemented.');
    }
  }

  var service = MailService.create(rpc, true, false);
  service.addMailbox(RequestType.create(), function (err, res) {
    console.log(err);
  });
});

mailservice.proto:

syntax="proto3";

message Mailbox {
  required string name = 1;
  repeated Mail messages = 2;
}

message Mail {
  string subject = 1;
  string body = 2;
}

message RequestType {}
message ResponseType {}

service MailService {
  rpc addMailbox(RequestType) returns (ResponseType);
  rpc removeMailbox(RequestType) returns (ResponseType);
}

@pronebird
Copy link
Author

pronebird commented Dec 14, 2016

Hey, quick question:

so with Protobuf.js I can create RPC server in Node.js and that's pretty clear, however, how do I define an RPC client? Do I have to funnel data to remote RPC and pass back results somehow? I use ZeroMQ as transport to pass data between processes. As I understand there is no Rpc channel/controller for that so I have to implement all of that myself?

i.e:

message RpcCall {
  string method = 1;
  bytes message = 2;
}

@dcodeIO
Copy link
Member

dcodeIO commented Dec 14, 2016

There is an example in the README.

There is also a streaming rpc example here.

It basically works the same way on a client or a server. You just have to implement rpcImpl accordingly.

@pronebird
Copy link
Author

@dcodeIO thanks. I figured that it works on network level so it was easy to wire up everything!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants