trooba-grpc-transport
Version:
gRPC transport for trooba pipeline
212 lines (173 loc) • 7.22 kB
Markdown
# trooba-grpc-transport
[](https://greenkeeper.io/)
[](https://codecov.io/gh/trooba/trooba-grpc-transport)
[](https://travis-ci.org/trooba/trooba-grpc-transport) [](https://www.npmjs.com/package/trooba-grpc-transport)
[](http://npm-stat.com/charts.html?package=trooba-grpc-transport)
[](https://snyk.io/test/github/trooba/trooba-grpc-transport)
gRPC transport for [trooba](https://github.com/trooba/trooba) pipeline.
The module provides a *client* and *service* side gRPC transport implementation for [trooba](https://github.com/trooba/trooba) pipeline.
## Get Involved
- **Contributing**: Pull requests are welcome!
- Read [`CONTRIBUTING.md`](.github/CONTRIBUTING.md) and check out our [bite-sized](https://github.com/trooba/trooba-grpc-transport/issues?q=is%3Aissue+is%3Aopen+label%3Adifficulty%3Abite-sized) and [help-wanted](https://github.com/trooba/trooba-grpc-transport/issues?q=is%3Aissue+is%3Aopen+label%3Astatus%3Ahelp-wanted) issues
- Submit github issues for any feature enhancements, bugs or documentation problems
- **Support**: Join our [gitter chat](https://gitter.im/trooba) to ask questions to get support from the maintainers and other Trooba developers
- Questions/comments can also be posted as [github issues](https://github.com/trooba/trooba-grpc-transport/issues)
## Install
```
npm install trooba-grpc-transport --save
```
## Usage
#### Service invocation
```js
var port = 50001;
var grpcTransport = require('trooba-grpc-transport');
require('trooba')
.use(grpcTransport, {
protocol: 'http:',
hostname: 'localhost',
port: port,
proto: require.resolve('path/to/hello.proto'),
connectTimeout: 100,
socketTimeout: 1000
})
.build('client:default')
.hello('Bob', function (err, response) {
console.log(err, response)
});
```
#### Sample proto definition:
```js
syntax = "proto3";
// The hello service definition.
service Hello {
// Sends a greeting
rpc SayHello ( HelloRequest) returns ( HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
```
#### Trooba based service
```js
var pipeServer = Trooba.use(transport, {
port: port,
hostname: 'localhost',
proto: Grpc.load(require.resolve('./path/to/hello.proto'))
})
.use(function handler(pipe) {
pipe.on('request', (request, next) => {
// do something with request
console.log('gRPC request metadata:', request.headers);
next();
});
pipe.on('request:data', (data, next) => {
// do something with request stream data chunk
console.log('request chunk:', data);
next();
});
pipe.on('request:end', (data, next) => {
// do something with stream end
console.log('end of request stream');
next();
});
pipe.on('response', (response, next) => {
// do something with response
console.log('gRPC response metadata:', response.headers);
next();
});
pipe.on('response:data', (data, next) => {
// do something with response stream data chunk
console.log('response chunk:', data);
next();
});
pipe.on('response:end', (data, next) => {
// do something with end of response stream
console.log('end of response stream');
next();
});
})
.use(function controller(pipe) {
// handle request/response here
pipe.on('request', request => {
pipe.respond({
body: 'Hello ' + request.body.name
});
});
});
var app = pipeServer.build('server:default');
svr = app.listen();
console.log('toorba service is listening on port:', port);
```
## Architecture
The module exports service and client API which matches exactly the API provided by [gRPC](https://github.com/grpc/grpc/tree/master/src/node) module.
Once request/response/data chunk enters the trooba pipeline, it assumes more generic API and request like data structures.
Trooba framework does not dictate specific data structures that should be used for request/response/messages/stream objects. It assumes basic requirements and leaves everything else to the implementor of the transport.
This transport goes further and defines some specifics for data it operates with:
* Possible flows:
* request/response is a basic interaction between client and service
* request/stream is a flow where for a single request it results in response stream
* stream/response is a flow where for request stream the backend generates a single response
* stream/stream is a flow where for the request stream the backend generates a response stream
* All the above flows use request and response object to initiate the flow and streaming uses arbitrary data chunks
* Request object structure:
* **body** contains request data which is a message object in gRPC terms
* **headers** contains request headers that match gRPC metadata
* **path** matches gRPC package namespace and service name separated by '/'. For example:
```
'foo.bar.v1.Hello.sayHello' => '/foo/bar/v1/Hello/sayHello'
```
* Response object structure:
* **body** contains response data which is a message object in gRPC terms
* **headers** contains response headers that match gRPC metadata
* **status** is gRPC status
* Data chunk matches gRPC streaming data
The client transport uses two timeouts:
* connectTimeout sets the deadline for establishing the connection
* socketTimeout sets the deadline for response or any further response chunk; whenever a new chunk is received the transport resets the socket timeout
#### Advanced examples
For more advanced examples, please take a look at [unit tests](test)
You can also find an implementation of simple service router [here](test/fixtures/server).
* Router example:
```js
module.exports = function routes() {
var router = Router.create();
router.use({
path: 'com/xyz/helloworld/Hello/sayHello',
handle: require('./sayHello')
});
router.use({
path: 'Hello/sayHello',
handle: require('./sayHello')
});
router.use({
path: 'Hello/sayHelloAll',
handle: require('./sayHelloAll')
});
router.use({
path: 'Hello/beGreeted',
handle: require('./beGreeted')
});
return router.build();
};
```
* Service:
```js
var pipeServer = Trooba
.use(transport, {
port: 40000,
hostname: 'localhost',
proto: Server.proto
})
.use(routes());
// create an app
var app = pipeServer.build().create('server:default');
// start it
app.listen(() => {
console.log('The server is ready');
});
```