RPC allows one computer to call a subroutine in another computer. It is a high level model for client-server communication.
In a microservices, one could use RPCs and not HTTP APIs since RPCs have several advantages over HTTP APIs.
In the early days, there were protocols that were great for human to application communication, like Email etc, but none for computer-computer application protocols.
Just like we have procedure calls, (which are function calls), we have remote procedure calls which call the procedures that aren’t on the same machine, but are remote.
The idea was that, since in a local procedure call, the compiler gives us the ability to make the call, the idea was that the compiler could play a role in enabling remote procedure calls as well.
When the client sends a RPC, it blocks till the server sends the response back. The server receives the request and starts process execution. There has to be some code on the client machine that knows this is a RPC and makes the needed network communication and get the answer and present it as the return value of the procedure call.
The RPC framework knows which server to contact, which port to contact, how to serialize the call, marshal it’s arguments etc. On the server side, similar stub should be present to unmarshall the arguments etc.
Intro to gRPC - https://www.youtube.com/watch?v=RoXT_Rkg8LA
We know how apps talk to one another. It is SOAP, REST (HTTP + JSON). REST is just a architectural principle, about how to structure your API when you use HTTP+JSON etc.
REST is not that great, actually. It has some advantages:
- easy to understand - text based protocols
- great tooling to inspect and modify etc
- loose coupling b/w client and server makes changes relatively easy
- high quality implementation in every language.
It has disadvantages as well:
- No formal API contract - there is documentation (swagger etc), but no formal contract
- Streaming is difficult
- Bidirectional streaming not possible (that’s why we had to invent websockets etc)
- Operations are difficult to model (“restart the computer”, should that be a POST, GET call?)
- Many times your services are just HTTP endpoints, and don’t follow the REST principles nicely.
- Not the most efficient since we are using gRPC
GRPC solves all these problems. You first define a contract using GRPC IDL - GRPC Interface Definition Language.
service Identity { rpc GetPluginInfo(GetPluginInfoRequest) returns (GetPluginInfoResponse) {} rpc GetPluginCapabilities(GetPluginCapabilitiesRequest) returns (GetPluginCapabilitiesResponse) {} rpc Probe (ProbeRequest) returns (ProbeResponse) {} } message GetPluginInfoResponse { // The name MUST follow reverse domain name notation format // (https://en.wikipedia.org/wiki/Reverse_domain_name_notation). // It SHOULD include the plugin's host company name and the plugin // name, to minimize the possibility of collisions. It MUST be 63 // characters or less, beginning and ending with an alphanumeric // character ([a-z0-9A-Z]) with dashes (-), underscores (_), // dots (.), and alphanumerics between. This field is REQUIRED. string name = 1; // This field is REQUIRED. Value of this field is opaque to the CO. string vendor_version = 2; // This field is OPTIONAL. Values are opaque to the CO. map<string, string> manifest = 3; }
Here, we have define a Identity
service, which supports 3 procedure calls - GetPluginInfo
, GetPluginCapabilities
, Probe
.
See how the GetPluginInfo
takes in the GetPluginInfoRequest
and returns a GetPluginInfoResponse
. Then we defined what the GetPluginInfoResponse
looks like below that.
This is a formal definition, with types. We can run a compiler thru it.
protoc --proto_path=. --python_out=plugins-grpc:./py calls.proto
This will generate client side code that can call the RPC. Similarly, we can generate the server side code as well.
Grpc is the framework which makes the RPC possible, it is a implementation of the RPC protocol.
GRPC is a protocol built on top of HTTP/2 as the transport protocol. The messages that you send and receive are serialized using Protocol Buffers - you can use some other.
Clients open one long lived connection to a grpc server. A new HTTP/2 stream for each RPC call. This allows multiple simultaneous inflight RPC calls. Allows client side and server side streaming.
The compiler can generate stubs in 9 languages. The reference implementation of grpc is in C. Ruby, Python are all bindings to the C core.
Gprc supports plugabble middleware, on the client and server side which can be used to do logging etc.
Swagger solves some of the problems around contract, in that people can make swagger IDL like contracts, but it is still text based protocol, doesn’t solve bidirectional streaming. Also, the swagger IDL is verbose.
One problem is that you can’t call this from the browser. The fact that it relies on having an intimate control over the HTTP2 connection means you need to have a shim layer in between.
Many companies have exposed grpc APIs to the public - like Google Cloud (their pub sub api, speech recognition api) etc.
Grpc - gRPC Remote Procedure Calls
It is a recursive fullform. It is a open source, high performance “RPC framework”
It is the next generation of Snubby RPC build and used inside Google.
- defining a service in a
.proto
file using protocol buffers IDL - generate the client and server stub using the protocol buffer compiler
- extend the generated server class in your code to fill in the business logic
- invoke it using the client stubs
Google’s lingua franca for serializing data - RPCs and storage. It is binary (so compact), structures can be extended in backward and forward compatible ways.
It is strongly typed, supports several data types
Let’s write an example called Route Guide. There are clients traveling around and talking to a central server. Or, it can be 2 friends traveling along 2 different routes and talking to each other.
We have to decide: what types of services do we need to expose? What messages to send?
syntax = "proto3"; // Interface exported by the server. service RouteGuide { // A simple RPC. // // Obtains the feature at a given position. // // A feature with an empty name is returned if there's no feature at the given // position. rpc GetFeature(Point) returns (Feature) {} // A Bidirectional streaming RPC. // // Accepts a stream of RouteNotes sent while a route is being traversed, // while receiving other RouteNotes (e.g. from other users). rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} } // Points are represented as latitude-longitude pairs in the E7 representation message Point { int32 latitude = 1; int32 longitude = 2; } // A feature names something at a given point. // // If a feature could not be named, the name is empty. message Feature { // The name of the feature. string name = 1; // The point where the feature is detected. Point location = 2; } // A RouteNote is a message sent while at a given point. message RouteNote { // The location from which the message is sent. Point location = 1; // The message to be sent. string message = 2; }
Grpc supports 2 types of RPCs:
- unary
- client sends a request
- server sends a response
- client streaming rpc
- client sends multiple messages
- server sends one response
- server streaming rpc
- client sends one response
- server sends multiple messages
- bi-directional streaming rpc
- client and server independently send multiple messages to each other
Now running the proto compiler on this will give you the client and server stubs. You just have to implement the business logic using these stubs.
Grpc is extensible:
- interceptors
- transports
- auth and security - plugin auth mechanisms
- stats, monitoring, tracing - has promotheseus, zipkin integrations
- service discovery - consul, zookeeper integrations
- supported with proxies - envoy, nginx, linkerd
Grpc has deadline propagation, cancellation propagation
The wire protocol used by grpc is based on HTTP/2 and the specification is well established.