Description: https://images.manning.com/360/480/resize/book/0/fb100d0-fa71-4eb2-bbd9-572eadb5b3a4/Babal-MEAP-HI.png

An excerpt from gRPC Microservices in Go by Hüseyin Babal

This excerpt covers:

§ Introducing Go gRPC Microservices

§ Comparing gRPC with REST

§ Understanding when to use gRPC

§ Better understanding of gRPC microservices with production-grade use-cases

Read it if you have a basic knowledge of Go and want to build production-grade microservices with the gRPC framework.


Take 25% off gRPC Microservices in Go by entering fccbabal into the discount code box at checkout at manning.com.


Good architecture design and proper technology selection helps a lot to build a high-quality product by eliminating repetitive work and providing best toolkit for software development and maintenance. The Go language is a good candidate for building high-performance Cloud-Native distributed applications like Microservices in Kubernetes on a large scale. Microservices with gRPC communication has already enabled lots of companies to implement their products with small services based on their business capabilities, and let those services communicate with each other and with public world smoothly. With the help of Go, the distribution of those services becomes easier due to its fast compilation, ability to generate executable binaries, and many other reasons that we will see in detail with real-life examples throughout the book.

gRPC is an open-source Remote Procedure Call framework initially developed by Google in 2015 that basically helps you to connect services with built-in support for load balancing, tracing, fault tolerance and security. The main power of this framework comes from being able to generate server and client stubs (an object on the client side that implements same methods as the service) for multiple languages that you can use in your consumer project to call remote service methods and in your server project to define business logic behind those service methods.

Microservice architecture is another form of service-oriented architecture that defines applications as loosely coupled, fine-grained services that can be implemented, deployed, and scaled independently.

The main goal of this book is to provide production-grade best practices for gRPC Microservices in a way that by the end of this book, you will have the self-confidence to implement the entire system on your own.

Benefits of gRPC Microservices

Within a typical monolithic application, calling a different business action like calling payment service from checkout service means accessing a class method in separate module which is very easy. If you use microservices, such calls will be converted to a network communication. It can be a call over TCP, HTTP or some sort of event queue to exchange data between services. Handling network calls are more challenging then calling another class method, which can be handled with a simple error handling mechanism like try catch blocks. Even Monoliths are easy to use at first, you may need to decompose them for several reasons, including slow deployments and inefficient resource utilization that clearly affects the feature development and product maintenance. This does not mean Monoliths are bad and Microservices are good; on the contrary, Microservices bring their own challenges like inter-service communication. With the help of gRPC, most of the challenges in Microservices like handling network failures, applying TLS (Transport Layer Security) to service communications can be eliminated. By using the built-in features in gRPC, you can improve both the reliability of your product and the productivity of your entire team by using Go Microservices.

Performance

gRPC provides better performance and security than other protocols like REST with JSON or XML communication, as it uses Protocol Buffers and HTTP/2 over TLS. Protocol Buffers, also known as Protobuf, is a language and platform neutral mechanism for serializing a structural data. This mechanism empowers gRPC to serialize messages into small and compact messages on both server and client side quickly. In the same way, HTTP/2 enables the performance with server-side push, multiplexing and header compression.

Code Generation & Interoperability

Let say that you have Checkout Service and Payment Service that a customer can check out a basket and call a Payment Service to pay for that product within as stated in basket. To access Payment Service, you need to have request and response models on some place like a shared library so that you can access them easily. Reusing a shared request and response models seems handy in Microservices, but actually it is not a good practice, especially if you are using different languages for each microservices. Duplicating models in Checkout Service, which is typically creating another data class to build request object and deserialize response object into, is better choice instead. This is all about preventing a wrong abstraction, as you may already heard the statement “A little duplication is far cheaper than wrong abstraction” (https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction). There is an easier way, which is choosing gRPC to define your messages and generate client stubs so that you can inject this dependency and use it directly in the language you prefer.

gRPC tools and libraries are compatible with multiple platforms and languages including Go, Java, Python, Ruby, Javascript, C# and more. Having the Protobuf binary wire format, the format of data as it travels on wire like in a network, and well-designed code generation for almost all platforms enables developers to build high-performance applications while keeping cross-platform support.

gRPC is getting more popular (https://star-history.com/#grpc/grpc&Date) since you can easily generate client stubs to provide an SDK of your services within different languages. The only thing you need to decide is what kind of business objects you need to have. Once you decide on which fields you need for a Checkout model, then you can introduce respective request and response models. Keep in mind that those objects are just definitions, which is called IDL (Interface Definition Language), independent from any language specification. After you define your message specifications, you can generate language specific implementations so that any consumer can depend on that source. This also means that development language on server side can be different on client side, since server side methods can be generated as stubs on the client side for specific languages supported by gRPC.

Beside business objects, you can also define service methods and generate actual implementations in the same way. Those service functions can be called after you initialize gRPC client on consumer side, again this client is generated out of the box.

Fault Tolerance

Fault tolerance is the ability of a system to continue operating despite system failures. An idempotent operation is the one that has no additional effect even it is called more than once. Idempotency is key to successful fault-tolerant environment since, you need to be sure that, once you retry an operation with same parameters in case of failure or not having expected state, it shouldn’t change the content of actual resource. For example, we may want to retry a user delete operation in case of network failure on response. If the operation returns same result even if you call it more than once, we say this operation is idempotent.

If an operation is not a good fit for idempotency use-case, then you need to provide proper validation errors in a response message that helps you know when to stop the retry operation. Once you guarantee this idempotency or proper validation, it is just a definition of retry policy on gRPC side. Fault tolerance also focuses on other topics like Rate Limiting, Circuit Breaker, and Fault Injection.

Security

In most of the systems, you may need to setup a security layer to protect your product against unverified sources. gRPC encourages HTTP/2 over SSL/TLS to authenticate and encrypt exchanged data between client and server. More specifically, you can easily set it up that authentication system by using SSL/TLS, ALTS (Application Layer Transport Security), or a Token Based Authentication System.

Streaming

Sometimes you may need to divide response data into several chunks to provide them to the user in a paginated way to reduce bandwidth and return them to the user quickly. Moreover, if they are only interested in certain pages, then it is not meaningful to return all the data at once. In gRPC, besides the pagination, you can also stream this data to the consumer instead of forcing user to do pagination to iteratively get data. Streaming shouldn’t be necessarily on the server side, it can be also on client side, or even in both sides at the same time, which is called bi-directional streaming. In a typical streaming use case, you open the connection once and the data will be streamed through this opened connection. You will see different kinds of usages of streaming use-cases while we are implementing a complete application within this book.

REST vs.  gRPC

REST (Representational State Transfer) is widely adopted protocol for microservices, but you may start to think about using gRPC if you have strict requirements like low-latency, multi-language system support, etc. REST is based on HTTP 1.0 Protocol that lets you exchange messages in JSON or XML format between client and server. On the other hand, gRPC is based RPC (Remote Procedure Call) architecture that uses Protocol Buffers binary format to exchange data over HTTP 2.0 Protocol. This does not mean that REST is not compatible with HTTP 2.0, you can setup your REST services based on that protocol with your own custom implementation where it is a built-in feature in gRPC.

Since gRPC has built-in HTTP 2.0 support, you can also make use of Unary and Bi-directional streaming between clients and servers that ends up very fast communication. With default settings of REST services, multiple client server communications can introduce a delay to your overall system performance.

There are also cases where REST is more beneficial than gRPC, for example REST protocol is supported in all kinds of browsers. Since gRPC support is very limited, you may need to use some sort of proxy layer to do conversion between HTTP 1.0 and HTTP 2.0.

gRPC has lots of advantages like being able to define messages to exchange data between services easily. However, when it comes to readability, JSON and XML usage in REST have advantages like being able to change it freely if there is no explicit business validation for the changed fields, whereas you need to follow some rules in gRPC to do a change. We will explain this in Chapter 5 in detail.

gRPC has built-in client and server stub generation mechanism where you need to use a framework in REST like Swagger Codegen to generate client-side models. This becomes critical especially once you have multiple services and maintain multiple SDKs at the same time for your customers.

Now that we understand the differences between REST and gRPC, let’s take a look at when it would make sense to use gRPC.”

When to Use gRPC

If you have strict requirements for browser support, then you need to think of using REST, since you will end up setting up another layer for conversion between HTTP/2 and HTTP/1. However, you can still use gRPC for inter-service communication and attach a gRPC load balancer (https://aws.amazon.com/blogs/aws/new-application-load-balancer-support-for-end-to-end-http-2-and-grpc/) to that service pool for exposing API to the public to have REST compatibility. There are other alternatives like Twirp(https://github.com/twitchtv/twirp), an RPC framework built on Protobuf. Twirp lets you enable REST layer for gRPC services in a way that you can access your endpoints like in the following example to send a POST request with a JSON payload.

 
 curl -X "POST" \
       - H "Content-Type: application/json" \
       -d '{"name": "dev-cluster"}' \    http://localhost:8080/twirp/github.com/huseyinbabal/microservices-proto/cluster/Create
  

Polyglot development environments are the best places for gRPC integrations, since being able to use the Python client within Checkout Service to access Payment Service, which is written by using Java, is very easy with client stub generation. You can apply the same strategy to your SDK generations for public consumers. Also, whenever you change your service definitions, the test would start to fail in the client side which is a good verification mechanism for your microservices.

gRPC may not be the proper selection for very simple applications like startup projects that contain only 1-2 services, since maintaining the proto files that contains service definitions is not easy, especially for inexperienced users.

It is fine to use gRPC communication between internal services, but it may not be ideal to expose a gRPC interface to customers, especially if there is no SDK for client for gRPC service communication. If you prefer to expose gRPC without maintaining the SDKs for your consumers, then it would be better to share your service definitions with them or provide a clean explanation about how to make gRPC calls to your gRPC services.

This book contains lots of explanations, code examples, tips & tricks supported by real-life examples that can be very useful for the following roles.

  • Developers who don’t know Go or Microservices: can take advantage of starting with introductory chapters about Go, Microservice, gRPC, and learn production-grade techniques for gRPC Go Microservices. For the readers who already know Microservice Architecture, can refresh their knowledge with the resources described in Go which can be easily adapted to any other language they use currently.
  • Engineering Managers: can improve developer productivity within their teams by adding the best practices described here into their playbooks. Applying practices will introduce good visibility over the entire product that will help to onboard new employees to the team easily.
  • Software Architects: there are lots of handy examples and architectural designs which are potential references to their decisions for new products or features.

There will be production-grade examples in this book in the following format.

  • Completed project at the end of this book
  • Code examples to better understand a specific topic and how it works
  • Automation examples especially with Github Actions to reduce repetitive operations
  • Preparing artifacts for deployment
  • Security best practices

Production-grade Use-cases

As shown in Figure 1, we will try to create an e-commerce product in this book with Go gRPC microservices that is automated within a proper CI/CD pipeline and lives in a Kubernetes environment. In the book, we’ll visit critical parts of the diagram to see how important they are for a typical development lifecycle, how gRPC makes those parts easier to handle, and which technologies to use where.


Figure 1. Architecture diagram of an e-commerce product built with Go Microservices on top of Kubernetes including CI/CD flow and Observability


Summary

  • gRPC performs well in inter-service communications because it uses binary serialization for the data and transfers it through the HTTP/2 protocol.
  • gRPC allows you to do client streaming, server streaming, and bi-directional streaming that allows you to send multiple requests or receive multiple responses in parallel.
  • Stable Client–Server interaction in gRPC Microservices are very easy to accomplish because of automatic code generation.
  • REST is very popular especially due to its wider browser support, but you can still use a gRPC web proxy (e.g. https://github.com/grpc/grpc-web) for REST-to-gRPC conversion.
  • Go is one of the best languages for cloud-native applications, like microservices in kubernetes, due to its high portability.
  • Using HTTP/2 over SSL/TLS end-to-end encryption connection in gRPC eliminates most of the security concerns for a Microservice
  • By the end of the book you will know all of this inside-out!

For more, check out the book here.