Hi,
I've been playing around with gRPC to find, that it kept every promise google and the gRPC team has been advertising. It also plays extremely well in a kubernetes environment and it is pretty obvious why google has been using it.
Has there been any discussion about this within the feathers community? Has anybody got any example code I could maybe look at how they solved the problem implementing it into the feathers architecture? I have some quick n' dirty work in progress and will share more of it, once I'm happy with it. I got the gRPC "hello world" tutorial working with this.
'use strict';
// Every Feathers Service needs to have a 'SERVICENAME.proto' file as well as 'SERVICENAME.grpcServer.js' file for the functions. They are then loaded on service start.
const fs = require("fs");
const path = require("path");
const grpc = require('grpc');
exports.default = grpcServer;
function grpcServer(){
return function(){
let app = this;
const SERVICES_PATH = __dirname + '/../services/';
let protoArray = [];
function jsUcfirst(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function loadFunctions(thePath,theFile){
var grpcServerFileName = thePath+'/'+theFile+'.grpcServer.js';
try{
fs.accessSync(grpcServerFileName, fs.constants.R_OK | fs.constants.W_OK);
return require(grpcServerFileName)(app);
}catch(e){
return {};
}
}
function loadProto(){
var files = fs.readdirSync(SERVICES_PATH);
files.forEach(function(file){
var pathNameToProtoFile = path.join(SERVICES_PATH, file)
var protoFileName = pathNameToProtoFile+'/'+file+'.proto';
try{
fs.accessSync(protoFileName, fs.constants.R_OK | fs.constants.W_OK);
var proto = {
name: file,
ucname: jsUcfirst(file),
path: protoFileName,
functions: loadFunctions(pathNameToProtoFile,file)
};
protoArray.push(proto);
}catch(e){}
});
main();
}
function main() {
var server = new grpc.Server();
protoArray.forEach(function(element, index, array){
console.log(element);
let PROTO_PATH = element.path;
let protoName = element.name;
let protoNameUc = element.ucname;
let functions = element.functions;
let proto = grpc.load(PROTO_PATH);
let grpcService = proto[protoName][protoNameUc].service;
server.addService(grpcService, functions);
})
server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
server.start();
}
loadProto()
}
}
module.exports = exports['default'];
````
'use strict';
// All proto files need to be present in the 'protoClients' file. In the config all services must be defined. They get loaded automatically into the app.grpcClient object and can then be called from within the app
const grpc = require('grpc');
exports.default = grpcCLient;
function jsUcfirst(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function grpcCLient(){
return function(){
let app = this;
let grpcConfig = app.get('grpcClient');
app.grcpClient = {};
grpcConfig.forEach(function(element, index, array){
var service = element.service;
var serviceClass = element.endpoint;
var classFunction = jsUcfirst(element.endpoint);
var PROTO_PATH = __dirname + '/../../protoClients/' +serviceClass+ '.proto';
var proto = grpc.load(PROTO_PATH);
var protoClass = proto[serviceClass][classFunction];
var client = new protoClass(service + ':50051', grpc.credentials.createInsecure());
app.grcpClient[serviceClass] = client;
})
}
}
module.exports = exports['default'];
```
The tricky bit is to generate the proto definitions. What would be cool is, if the .proto files would be generated from a mongoose definition or something similar.
One of the key features of Feathers is that it is transport protocol agnostic and adding new ones is usually not too much work (see https://github.com/feathersjs/feathers-rest/ and https://github.com/feathersjs/feathers-socketio). If you are interested in taking this on we can definitely point you in the right direction.
However, I don't think the team will get to it any time soon. We usually keep proposals like this open for a month or two but will close it if there is no further activity.
I don't have too much time on my hands and this was more prototyping, I'll see what I can do.
My friend is writing a book on Go, and he's really fond of gRPC. I'll ping him to see if he has any interest. Using gRPC could lead to a big performance boost for Feathers data-oriented microservices architecture
@agtorre any interest now or in the future? (I can help on the JS/Node side)
Hey, this seems pretty interesting. Would the idea be to have a grpc transport? Is it okay that grpc doesn't currently communicate with front-end applications?
@idibidiart I'm truly ignorant, here. How could using gRPC lead to a big performance boost? It seems to me like gRPC is just an implementation of RPC, basically calling remote functions on a local machine. What am I missing, here?
@agtorre maybe the Feathers client with feathers-authentication-client can be used as a gateway for gRPC to communicate with the client.
@marshallswain
"gRPC is faster than REST. Others have documented this, but in a nutshell, it runs on HTTP2 by default (see a comparison by Brad Fitz) and when using Google's protocol buffers (you don't have to) for encoding, the information comes on an off the wire much faster than JSON. There's also bidirectional streaming, header compression, cancellation propagation and more"
source: https://www.sajari.com/blog/grpc-and-displacement-of-rest-apis
IMO, main benefit to supporting gRPC is when aggregating services (making joins at service level rather than using FK relationships at db level), which is the architecture I'm using (based on Feathers+GraphQL) In that scenario, I'm making Promise-chained calls to compose app state (from data) in the GraphQL resolvers. Those calls are HTTP/REST and are very slow compared to having a pool of socket connections to services that are used from the GraphQL resolvers and using binary format for the request and response, but that would be too ad-hoc to build by hand and I believe gRPC gives us a standard way to higher performance service-level joins.
In my scenario, don't care about client to GraphQL service because that is just one call, while I have N calls from GraphQL resolver (where GraphQL is a Feathers service) to other Feathers services and it's those N calls that could be sped up significantly with gRPC.
I admit I've never played around with gRPC and all my conclusions are based on others' reported experience and the reading about how gRPC works. @agtorre has built gRPC based microservice architecture that is in production and at cloud scale. So he can provide more practical input here.
Excited about the prospect for a higher performance interface to Feathers services.
cc: @daffl @ekryski
Would it really be faster than just using websockets (which don't rely on HTTP at all other than the initial handshake)? It would be nice to see some benchmarks. Eventually it would definitely be nice to have a third high-performance transport mechanism. We already discussed things like ZeroMQ and other messaging services and we'd probably want to base the decision which one to choose on some benchmark and features comparisons.
It would not be necessarily faster than web sockets and a proprietary protocol but it will do a few things:
1) Use an industry standard (gRPC)
2) Bring more users to Feathers (from Go)
3) potentially inspire a Go implementation of Feathers
@daffl
"gRPC is faster than REST."
@idibidiart is right, it wouldn't be faster. The articles claiming that "gRPC is faster than REST" are playing with some sloppy language. What they're really saying is the HTTP2 is faster than HTTP1, which of course makes perfect sense. So, it's the actual transport that makes the difference, not the message format on top of it. If I baked WebSocket and REST support into my fork of BitCoin core, it wouldn't make sense to claim that "REST is faster than RPC!", when all I've done is switch to using WebSockets, but that's what they're all doing.
Overall, it would be nice to have an adapter for gPRC. Then we could give everybody lessons on how to map RPC to rest and vice versa, and make it all work with Feathers. That would be a nice contribution.
It's not just HTTP2, it's also protocol buffers that make gRPC fast. This is also the reason it's not currently compatible with client applications without a translation layer like https://github.com/improbable-eng/grpc-web.
I don't see a possibility of any speed increase coming from using protocol buffers. If anything, when using node, their use would likely result in a speed decrease. They're just a message format that's larger and more complex, but more powerful than plain JSON. It's pretty similar to using JSON with JSON Schema.
Looking at this article promoting protocol buffers, a Feathers application using Sequelize, Mongoose or JSON-Schema already enjoys all of the listed benefits of Protocol Buffers. Below is a screenshot from that article. Using Feathers pretty much takes care of the top five bullet points, and in most applications makes the sixth one pointless, anyway.

In Node land, it looks like implementing Protocol Buffers is probably a great idea for interop with other non-Node servers with the Node server working as the client.
I didn't close it because I thought it was a bad suggestions but we have a lot of other things on our plate and it looks like nobody is planning on taking this on anytime soon.
As important as performance discussions are are at appropriate times, I encourage anybody who would like to see this to look at the existing providers (feathers-rest, feathers-socketio, feathers-primus) and give it a stab.
Here is a template for creating your own provider:
module.exports = function(options) {
return function() {
const app = this;
const oldsetup = app.setup;
app.providers.push(function (path, service, options) {
// Do GRPC setup for specific service here
app.grpc
});
// For general setup (e.g. starting a server etc.)
// override `app.setup`
app.setup = function(... args) {
const result = oldsetup.apply(this, args);
// Do server setup things here
this.grpc = new GRPServer();
return result;
}
}
}
And can be used like you are used to via
cont grpc = require('./grpc');
app.configure(grpc());
I can definitely see a potential use connecting a feathers app to ML/AI services written in python, etc. using this grpc.
i say grpc support makes no sense as grpc is a replacement for feathers itself :D It implements the everything is service mythology and so on. a unified client all this
Most helpful comment
I didn't close it because I thought it was a bad suggestions but we have a lot of other things on our plate and it looks like nobody is planning on taking this on anytime soon.
As important as performance discussions are are at appropriate times, I encourage anybody who would like to see this to look at the existing providers (feathers-rest, feathers-socketio, feathers-primus) and give it a stab.
Here is a template for creating your own provider:
And can be used like you are used to via