Before I begin, here’s some context for some people who have never heard or used any RPC.
RPC means Remote Procedure Call, so some code will be calling a procedure that will be executed somewhere else.
RPC is just a general name and can be implemented in many ways. Here are some important characteristics:
- Request-response protocol
- Used over TCP / HTTP
- Language agnostic
- Payload agnostic
When should you use RPC?
If we are talking about executing some procedure somewhere else, that sounds like distributed computing to me. That is, if you have some micro services and want to make their communication easier and more efficient, RPC may be the way to go.
How do you do it?
Here are some implementations of RPC and their positive and negative characteristics.
1. Go default RPC
func (c *Client) Call(method String, args interface{}, reply interface{}) error
Pros:
- Simple, doesn’t add any dependencies
- Easy to use
Cons:
- Unsafe (method is a string, args and reply are interfaces)
- Hard to maintain (any change may cause huge consequences)
2. gRPC
Easily the more well know implementation, gRPC was created by Google and internally used, improved and tested for a decade before becoming open source. That means it is very robust and reliable, so many big companies like Netflix, Cockroach Labs, and Cisco are using it.
It uses a code generation tool that creates client and server stubs and all types to make it safer. It’s important to note that objects are abstracted into services, and references into messages.
Pros:
- Code generation
- Type safe
- Great performance
Cons:
- It’s very hard to control the versions, every update can break everything
- Doesn’t use Go’s default HTTP implementation
- Only accepts binary messages, which makes it hard to debug
What makes it good?
Some comparisons (like this one) between REST and gRPC show how efficient it is, but why?
Protocol buffers (Protobuf)
The big difference in performance is due to this extremely efficient language created by Google. It compresses payload data into a very small binary or JSON.
It was created to be better than XML, also it is type safe. The downside is that both sides in the communication need to know the message formats to be able to understand and decode it.
package protoexample;
service Example {
rpc Procedure(Request) returns (Response) {}
}
message Request {
int32 first = 1;
int32 second = 2;
}
message Response {
Payload subfield;
}
message Payload {
int32 first = 1;
int32 second = 2;
}
2. Twirp
After a long time dealing with gRPC’s issues of version control, once I decided to search for alternatives and found Twitch’s Twirp. On their blog they posted:
Why not gRPC?
This code generation approach is not a novel idea at all. Google provides a framework, gRPC (https://github.com/grpc/grpc), which does a very similar thing, and gRPC has grown to be pretty prominent. In fact, we started out at Twitch using gRPC. It didn’t gain a lot of traction, though — we had some problems with it, and these problems spurred us to create Twirp.
Pros:
- Simpler approach, uses Go’s default HTTP implementation
- Easier to keep track of version control
- Accepts both binary and JSON payloads
- Generates a REST server if needed
- Accepts both HTTP1 and HTTP2
Cons:
- Does’t have support for bidirectional stream
Conclusion
There’s no silver bullet solution, but after considering all the pros and cons, I really liked using Twirp for my micro services. If you are interested, I wrote a simple example code to help you get started!
Read more
- https://medium.com/@EmperorRXF/evaluating-performance-of-rest-vs-grpc-1b8bdf0b22da
- https://pkg.go.dev/net/rpc
- https://developers.google.com/protocol-buffers
- https://grpc.io/about/
- https://github.com/StudioSol/go/tree/sspay
- https://twitchtv.github.io/twirp/docs/intro.html
- https://blog.twitch.tv/pt-br/2018/01/16/twirp-a-sweet-new-rpc-framework-for-go-5f2febbf35f/
Code example: