grpc / grpc-node

gRPC for Node.js
https://grpc.io
Apache License 2.0
4.43k stars 640 forks source link

poor performance vs Rest API #2054

Open amit034 opened 2 years ago

amit034 commented 2 years ago

trying to pipe 4 microservices with more then 50000 messages getting poor results comparing to REST API

I'm trying to learn about gRPC and use its advantages in the stream client & server side capabilities, and compare its performance with REST.

in my POC i have created an application that request for a movie to watch and get a recommendation.

The application, on receiving the request, fetches the list of movies categorized by the genre, then matches it against the user preferences and finally passes it over a recommendation engine to suggest a movie response back to the user.

A total of four microservices will be built for this solution. All interactions between the microservices will be based on gRPC.

the request stream: Movie Finder -> Movie Store -> User Preferences -> Recommender

    syntax = "proto3"; 
    enum Genre {
      COMEDY = 0;
      ACTION = 1;
      THRILLER = 2;
      DRAMA = 3;
    }
    message Movie {
      string title = 1;
      double rating = 2;
      Genre genre = 3;
      string description = 4;
    }
    message MovieFinderRequest {
      int32 user_id = 1;
      Genre genre = 2;
    }
    message MovieFinderResponse {
      Movie movie = 1;
    }
    service MovieFinderService {
      rpc findMovie(MovieFinderRequest) returns (MovieFinderResponse) {};
    }

    // MovieStoreService
    message MovieStoreRequest {
      Genre genre = 1;
    }
    message MovieStoreResponse {
      Movie movie = 1;
    }
    service MovieStoreService {
      // server streaming rpc call to receive a stream of movies
      rpc getMovies(MovieStoreRequest) returns (stream MovieStoreResponse) {};
    }

    //UserPreferencesService
    message UserPreferencesRequest {
      int32 user_id = 1;
      Movie movie = 2;
    }
    message UserPreferencesResponse {
      Movie movie = 1;
    }
    service UserPreferencesService {
      // Bidirectional streaming rpc call to receive a stream of movies shortlisted based on user preferences
      rpc getShortlistedMovies(stream UserPreferencesRequest) returns (stream UserPreferencesResponse) {};
    }
    //RecommenderService
    message RecommenderRequest {
      Movie movie = 1;
    }
    message RecommenderResponse {
      Movie movie = 1;
    }
    service RecommenderService {
      // client streaming request that receives a stream of movies and recommends one
      rpc getRecommendedMovie(stream RecommenderRequest) returns (RecommenderResponse) {};
    }
    pipeline(
        movieStoreClient.getMovies({genre}),
        new Transform({
            objectMode: true,
            transform({movie}, encoding, callback) {
                callback(null, {userId, movie});
            }
        }),
        userPreferencesClient.getShortlistedMovies(),
        recommenderClient.getRecommendedMovie((err, recommenderResponse) => {
            if (err) {
                console.error(err);
                return next({message: err.message})
            }
            return res.send(recommenderResponse);
        }),
        (err) => {
            if (err) {
                next({message: err.message})
            }
        }
    );

I've assumed the gRPC will beat Rest API due to the streaming and the lightweight payload. but it wasn't the case, the REST API returned back in 600ms and the gRPC in 4s.

I was thinking to use makeGenericClientConstructor to prevent from serialize\deserialize the request and response down the wire to reduce the message data conversation time but unfortunately I need to make manipulations on the data to add user info, so I must deserialize it.

what am I missing ?

here is my POC if someone have the time to look at it. https://github.com/amit034/grpc-node-poc

HsinHeng commented 2 years ago

trying to pipe 4 microservices with more then 50000 messages getting poor results comparing to REST API

I'm trying to learn about gRPC and use its advantages in the stream client & server side capabilities, and compare its performance with REST.

in my POC i have created an application that request for a movie to watch and get a recommendation.

The application, on receiving the request, fetches the list of movies categorized by the genre, then matches it against the user preferences and finally passes it over a recommendation engine to suggest a movie response back to the user.

A total of four microservices will be built for this solution. All interactions between the microservices will be based on gRPC.

the request stream: Movie Finder -> Movie Store -> User Preferences -> Recommender

    syntax = "proto3"; 
    enum Genre {
      COMEDY = 0;
      ACTION = 1;
      THRILLER = 2;
      DRAMA = 3;
    }
    message Movie {
      string title = 1;
      double rating = 2;
      Genre genre = 3;
      string description = 4;
    }
    message MovieFinderRequest {
      int32 user_id = 1;
      Genre genre = 2;
    }
    message MovieFinderResponse {
      Movie movie = 1;
    }
    service MovieFinderService {
      rpc findMovie(MovieFinderRequest) returns (MovieFinderResponse) {};
    }

    // MovieStoreService
    message MovieStoreRequest {
      Genre genre = 1;
    }
    message MovieStoreResponse {
      Movie movie = 1;
    }
    service MovieStoreService {
      // server streaming rpc call to receive a stream of movies
      rpc getMovies(MovieStoreRequest) returns (stream MovieStoreResponse) {};
    }

    //UserPreferencesService
    message UserPreferencesRequest {
      int32 user_id = 1;
      Movie movie = 2;
    }
    message UserPreferencesResponse {
      Movie movie = 1;
    }
    service UserPreferencesService {
      // Bidirectional streaming rpc call to receive a stream of movies shortlisted based on user preferences
      rpc getShortlistedMovies(stream UserPreferencesRequest) returns (stream UserPreferencesResponse) {};
    }
    //RecommenderService
    message RecommenderRequest {
      Movie movie = 1;
    }
    message RecommenderResponse {
      Movie movie = 1;
    }
    service RecommenderService {
      // client streaming request that receives a stream of movies and recommends one
      rpc getRecommendedMovie(stream RecommenderRequest) returns (RecommenderResponse) {};
    }
    pipeline(
        movieStoreClient.getMovies({genre}),
        new Transform({
            objectMode: true,
            transform({movie}, encoding, callback) {
                callback(null, {userId, movie});
            }
        }),
        userPreferencesClient.getShortlistedMovies(),
        recommenderClient.getRecommendedMovie((err, recommenderResponse) => {
            if (err) {
                console.error(err);
                return next({message: err.message})
            }
            return res.send(recommenderResponse);
        }),
        (err) => {
            if (err) {
                next({message: err.message})
            }
        }
    );

I've assumed the gRPC will beat Rest API due to the streaming and the lightweight payload. but it wasn't the case, the REST API returned back in 600ms and the gRPC in 4s.

I was thinking to use makeGenericClientConstructor to prevent from serialize\deserialize the request and response down the wire to reduce the message data conversation time but unfortunately I need to make manipulations on the data to add user info, so I must deserialize it.

what am I missing ?

here is my POC if someone have the time to look at it. https://github.com/amit034/grpc-node-poc

REST/HTTP 1.1 is like gRPC unary protocol. However, Your many services use stream protocol in request & response. I think you need to use Unary Protocol in all services as a benchmark.

murgatroid99 commented 2 years ago

I looked at your POC repository and I can't find the code that actually performs the performance test. Can you point out where that code is so that we can see the usage pattern that resulted in the performance you observed?

bmviniciuss commented 2 years ago

I'm facing some similar issue where the REST implementation is faster than the grpc one. My benchmark code is: https://github.com/bmviniciuss/tcc/blob/main/benchmarks/src/create-card/create-card.js

I'm seeing gRPC close to or worst than HTTP. Am I missing something?

The gateway calls either the HTTP or gRPC: HTTP: https://github.com/bmviniciuss/tcc/blob/main/gateway/src/adapters/card/AxiosHttpCardAPI.ts gRPC: https://github.com/bmviniciuss/tcc/blob/main/gateway/src/adapters/card/GRPCCardAPI.ts

MartianH commented 1 year ago

Experiencing the same issue. gRPC performance is significantly worse than rest (using "@grpc/grpc-js": "^1.8.15" with "@nestjs/core": "^9.0.0"). Downgrading all the way to 1.2.x did nothing.