gRPC vs REST: Comparing API architectures for microservices

Understand the differences between gRPC and REST and when to use each for your microservices architecture.

Background

When building microservices, one of the key decisions is choosing the right API architecture. Two popular choices are REST (Representational State Transfer) and gRPC (gRPC Remote Procedure Call). While REST has been the dominant approach for many years, gRPC has been gaining traction, especially in high-performance, polyglot environments.

REST is an architectural style that defines a set of constraints for creating web services. It relies on HTTP and JSON, making it simple, flexible, and browser-friendly. REST has been widely adopted and is supported by virtually every programming language and framework.

gRPC, on the other hand, is a modern, open-source remote procedure call framework developed by Google. It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as bi-directional streaming and integrated authentication.

The competition

If you’re considering gRPC for your microservices, you might ask: “Why not stick with REST? It’s been around longer and has a larger ecosystem.” A valid question. REST is indeed more mature and widely adopted. However, gRPC offers some distinct advantages:

  1. Performance: gRPC uses HTTP/2, which supports multiplexing, compression, and binary data transfer. This can result in significant performance improvements over REST, especially for high-volume, low-latency services.
  2. Strong Typing: gRPC uses Protocol Buffers, which provide strong typing and efficient serialization. This can catch errors early and make the system more maintainable.
  3. Bi-Directional Streaming: gRPC supports bi-directional streaming, allowing both the client and the server to send a sequence of messages. This is useful for real-time, event-driven architectures.
  4. Code Generation: gRPC automatically generates client and server stubs in multiple languages from the Protocol Buffer definitions, reducing boilerplate and making it easy to create polyglot systems.
  5. Integrated Authentication: gRPC has built-in support for authentication, including SSL/TLS and token-based authentication.

Getting Started with gRPC

Now that we’ve covered the background and advantages of gRPC, let’s dive into creating a simple gRPC service. We’ll build a basic calculator service that can add two numbers.

Setting Up

First, make sure you have Protocol Buffers installed. You can find installation instructions in the official Protocol Buffers documentation.

Next, let’s install the gRPC tools for your language of choice. For this example, we’ll use Python. Install the gRPC Python package:

pip install grpcio-tools

Defining Our Service

Let’s define our calculator service using Protocol Buffers. Create a new file named calculator.proto:

syntax = "proto3";

package calculator;

service Calculator {
  rpc Add (AddRequest) returns (AddResponse) {}
}

message AddRequest {
  int32 a = 1;
  int32 b = 2;
}

message AddResponse {
  int32 result = 1;
}

This defines a Calculator service with a single Add method that takes two integers and returns their sum.

Generating Code

With our service defined, we can generate the gRPC client and server code. Run the following command:

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. calculator.proto

This generates two files: calculator_pb2.py, which contains the message classes, and calculator_pb2_grpc.py, which contains the client and server classes.

Implementing the Server

Let’s implement our calculator server. Create a new file named calculator_server.py:

from concurrent import futures
import grpc
import calculator_pb2
import calculator_pb2_grpc

class CalculatorServicer(calculator_pb2_grpc.CalculatorServicer):
    def Add(self, request, context):
        return calculator_pb2.AddResponse(result=request.a + request.b)

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    calculator_pb2_grpc.add_CalculatorServicer_to_server(CalculatorServicer(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    server.wait_for_termination()

if __name__ == '__main__':
    serve()

This creates a CalculatorServicer that implements the Add method, and a serve function that starts the gRPC server.

Implementing the Client

Now, let’s implement a client that calls our calculator service. Create a new file named calculator_client.py:

import grpc
import calculator_pb2
import calculator_pb2_grpc

def run():
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = calculator_pb2_grpc.CalculatorStub(channel)
        response = stub.Add(calculator_pb2.AddRequest(a=10, b=5))
    print("10 + 5 = ", response.result)

if __name__ == '__main__':
    run()

This creates a gRPC channel, stubs the Calculator service, and calls the Add method with the arguments 10 and 5.

Running Our Service

To run our service, first start the server:

python calculator_server.py

Then, in another terminal, run the client:

python calculator_client.py

You should see the output:

10 + 5 =  15

Advanced Features

While our calculator service demonstrates the basics, gRPC offers many more advanced features for building robust, high-performance services. Here are a few to consider:

Streaming

gRPC supports bi-directional streaming, allowing both the client and the server to send a sequence of messages. There are three types of streaming:

  1. Server-side streaming: The client sends a request and gets a stream to read a sequence of messages back. Useful for real-time updates or large data transfers.
  2. Client-side streaming: The client writes a sequence of messages and sends them to the server, again using a provided stream. Useful for uploading large data or real-time data ingestion.
  3. Bi-directional streaming: Both sides send a sequence of messages using a read-write stream. Useful for real-time interactions or gaming protocols.

Error Handling

gRPC has a standardized way of handling errors. It uses the Status message to convey error details and the status package in each language to handle errors.

from grpc import StatusCode

def Add(self, request, context):
    if request.a < 0 or request.b < 0:
        context.set_code(StatusCode.INVALID_ARGUMENT)
        context.set_details('Inputs must be non-negative')
        return calculator_pb2.AddResponse()
    return calculator_pb2.AddResponse(result=request.a + request.b)

This code checks for negative inputs and returns an INVALID_ARGUMENT status if the precondition is not met.

Interceptors

Interceptors are a powerful mechanism in gRPC that allows you to intercept and modify requests and responses. They’re commonly used for logging, monitoring, authentication, or pre/post-processing.

import grpc

class LoggingInterceptor(grpc.ServerInterceptor):
    def intercept_service(self, continuation, handler_call_details):
        print(f"Received {handler_call_details.method} request")
        return continuation(handler_call_details)

server = grpc.server(futures.ThreadPoolExecutor(max_workers=10),
                     interceptors=(LoggingInterceptor(),))

This code defines a simple logging interceptor that logs every incoming request.

Conclusion

We’ve only scratched the surface of what’s possible with gRPC. Its focus on performance, strong typing, and streaming makes it a powerful choice for building efficient, scalable microservices.

While gRPC offers many benefits, it also comes with some trade-offs. It’s less browser-friendly than REST, has a smaller ecosystem, and can be more complex to set up and debug.

As with any architectural decision, it’s important to evaluate whether gRPC is the right fit for your specific needs. For internal microservices with high performance requirements, gRPC can be a great choice. For public-facing APIs that need to be accessible from browsers, REST might be a better fit.

If you’re interested in learning more about gRPC, I highly recommend the official gRPC documentation and the gRPC community. The gRPC blog is also a great resource for keeping up with the latest developments and best practices.

Happy coding!

関連記事

カテゴリー:

ブログ

情シス求人

  1. チームメンバーで作字やってみた#1

ページ上部へ戻る