Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduction to GRPC #237

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions posts/introduction-to-grpc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
---
authors:
- Kenny Hegeland
tags:
- gRPC
date: 2015-04-23T12:21:50.000Z
title: "Introduction to gRPC
image:
---

# What is gRPC?
gRPC (remote procedure call) is used for communicating between distributed systems (micro services).
Google created and originally used an RPC framework called stubby, which later was open sourced as gRPC.
hegek87 marked this conversation as resolved.
Show resolved Hide resolved
While using protobuffers is not mandatory to use gRPC, it is generally the easiest approach (as it
hegek87 marked this conversation as resolved.
Show resolved Hide resolved
works out of the box). You define a protobuf in a simple message format and use protoc (the protobuf
compiler) to generate code to handle serializing and creating your domain objects. You can also create
a service in the protobuf definition which allows generation of methods for the client to communicate
with the server.

The protobuf simply defines the contract between the services, and this allows us to be language agnostic.
Using libraries for specific language, you can generate language specific code and use a language specific
client to call the server. The server doesn’t need to know what language (this is also true of REST) the
client is using, all it needs is the serialized message (which it knows how to deserialize).

Now that we’ve discussed what gRPC is, let’s flesh out an example. We will create a simple example
application (available on GitHub - https://github.com/hegek87/derivative-solver) to compute the derivative
of simple polynomials. If you don’t remember how a derivative is calculated, we will quickly show the

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you using an example using polynomials? It couldn’t be something else?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particular reason, I just like math and thought this would be a simple to implement and explain example

general rule our server will use.

## Polynomials and Derivatives

Remember that a polynomial is a function of the form
> P(x) = c_nx^n + c_{n-1}x^{n-1} + …. + c_1x + c_0,
>
> where c_1, c_2, …, c_n are simple constants.

To give a concrete example, let’s use the polynomial `x^4 + 2x^2 - x - 1`. Next, let’s get a better
understanding of how to find the derivative of a polynomial. If you never learned this, it’s a pretty
robotic procedure that will hopefully be straightforward to understand. To calculate a derivative, we
simply multiply the exponent by the coefficient, and subtract one from the original exponent. Given
a simple polynomial, `4x^2`, we find the derivative by multiplying the exponent and coefficient,
`4*2 = 8`, then we subtract one from the exponent, `2-1=1`, giving us the derivative, `8x^1 = 8x`.
This procedure works for simple polynomials, and can be expanded for polynomials of more than one term.
As another example, consider `x^3 + 3x^2 + 3`. Remember that x^0 = 1, so `3 = 3x^0`, and we can
calculate the derivative as `3*0 = 0`. The full derivative of the equations leads to `3x^2 + 6x + 0 = 3x^2 + 6x`.


## Creating A Message Format
Now that we understand what a derivative is, let’s discuss how we represent a polynomial for our protobuf.
It seems like we have two simple ways to represent the polynomial, a naive way (which we handle in our rest
call) to do this is by sending it as a raw string. While this works, it could be cumbersome, as we will
need to find a way to split the string, parse the constants and exponents into numeric values and map back
to a string after calculating the derivative. Perhaps a more elegant solution is to send the constant as an
array, and the exponents as an array, this way we can get the constant values and exponent values without
any parsing on our side. Something like

```java
Message Polynomial {
hegek87 marked this conversation as resolved.
Show resolved Hide resolved
repeated coefficients = 1;
repeated exponents = 2;
}
```

Now that we have our message structure defined, we can compile to see the generated code. As expected, we
will see POJOs generated with setters, getters, and builders. Now that we've configured our message structure,
we can define our service method that will be called in order to actually make requests:
```java
service DerivativeSolver {
rpc findDerivative(Polynomial) returns (Polynomial) {}
}
```

Compiling using protoc once again, we will now have everything we need to set up our simple application.
hegek87 marked this conversation as resolved.
Show resolved Hide resolved

## Creating a gRPC Client and Server

Let’s discuss how we create a client to communicate with our server. Before we can create a client, we
must create a channel which contains host and port information of our server, the channel can also allow
us to easily add encryption, security, and more. For more information on what is available, look here:
https://grpc.github.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html. For our example, we will
create a simple channel that just sets a host, port (both of which will be grabbed from an application
properties file) and builds it for later use:

```java
ManagedChannelBuilder
.forAddress(applicationProperties.getGrpc().getHost(), applicationProperties.getGrpc().getPort())
.usePlaintext()
.build();
```

Now that we have created a channel, it is fairly simple to actually create a gRPC client. In our generated code,
we will find a static method named `newBlockingStub`. This method takes a channel (which we just created) as an
argument and provides us with a client to call our server. Here is how we will generally create a client:
```java
DerivativeSolverGrpc.DerivativeSolverBlockingStub client = DerivativeSolverGrpc.newBlockingStub(channel);
```

This creates a client, so now we can call our server. This is quite simple, as shown below, we create a polynomial,
then simply call the rpc method defined earlier through our newly created client.


```java
// these arrays are the same as our previous example
int[] coefficients = [1, 2, 1, 1]
int[] exponents = [4, 2, 1, 0]
// x^4 + 2x^2 + x + 1
Polynomial polynomial = Polynomial.coefficients(coefficients).exponents(exponents).build();
Polynomial derivative = client.findDerivative(polynomial);
```

Our server listens for requests and can calculate the derivative of a polynomial and return the new polynomial.

And our server:
```java
public void findDerivative(Polynomial polynomial, StreamObserver<Polynomial> responseObserver) {
if(polynomial.getCoefficientsCount() != polynomial.getExponentsCount()) {
RuntimeException err = new RuntimeException("Invalid polynomial definition - It should have the same amount of exponents and coefficients");
responseObserver.onError(err);
throw err;
}

List<Integer> coefficients = polynomial.getCoefficientsList();
List<Integer> exponents = polynomial.getExponentsList();

List<Integer> updatedCoefficient = new ArrayList<>();
List<Integer> updatedExponents = exponents.stream().map(exponent -> exponent - 1).collect(Collectors.toList());

for(int i = 0; i < coefficients.size(); ++i) {
updatedCoefficient.add(i, coefficients.get(i) * exponents.get(i));
}

responseObserver.onNext(Polynomial.newBuilder().addAllCoefficients(updatedCoefficient).addAllExponents(updatedExponents).build());
responseObserver.onCompleted();
}
```

Setting this all up requires deploying both the server application, and the client application, both of which will use
the same protobuf definitions. To run this locally, we've created simple docker files which do simple mapping of ports,
file systems and sets up a bridge network where they will communicate. In an actual deployed environment, we will
likely have to use some sort of DNS to reference the host.


Lots of code samples found in this article are snippets of our previously linked project which can be used as a
hegek87 marked this conversation as resolved.
Show resolved Hide resolved
reference. The actual application made available has some other stuff built on top, following familiar spring patterns.
hegek87 marked this conversation as resolved.
Show resolved Hide resolved
Among other things, we have a `@Configuration` class which is used to configure our gRPC server to be running inside
our spring application. In this configuratioon, `DerivativeCalculatorService` houses our previously shown derivative
logic.

```java
@Configuration
public class GrpcServerConfiguration {

private static final Logger LOGGER = LoggerFactory.getLogger(GrpcServerConfiguration.class);

private int port;
private Server server;
private DerivativeCalculatorService derivativeCalculatorService;

public GrpcServerConfiguration(ApplicationProperties applicationProperties, DerivativeCalculatorService derivativeCalculatorService) {
this.port = applicationProperties.getGrpc().getPort();
this.derivativeCalculatorService = derivativeCalculatorService;
}

@Bean
public Server grpcServer() throws IOException {
LOGGER.info("Starting GRPC Server");
this.server = ServerBuilder.forPort(this.port).addService((BindableService) derivativeCalculatorService).build();
this.server.start();
return this.server;
}
}
```

Now we can finally try our example out. First we will want to build our two applications with maven, using the common
hegek87 marked this conversation as resolved.
Show resolved Hide resolved
`mvn clean install`, then we can start our two images with the command `docker-compose -f docker-compose.yml up —build`.
Each project in our GitHub has its own docker compose file, one in server/docker-compose, and one in client/docker-compose.
With both up and running, we can send requests to our client (also a spring boot app), via a simple rest endpoint.
It takes a polynomial in the form of a string (which is cumbersome as we said before), converts to our protobuf format
and sends the polynomial to the server. Assuming these are both up and running, we simply run the following command
inside our terminal:

```
curl --location --request POST 'localhost:8081/example/calculate-derivative' \
--header 'Content-Type: application/json' \
--data-raw '{"polynomial": "1x^4 + 2x^2 + 1x^1 + 1x^0"}'
```

Feel free to play around with the example and try other simple polynomials. Some caveats when using this, be aware that
some requirements exist, for one, our polynomials we send include all exponents and coefficients (even 1 and 0), and
that’s why we see `1x^1 + 1x^0` instead of `x + 1`, this is simply due to our simple parsing of the string polynomial.
It is possible to handle these cases more elegantly, but it makes the parsing logic more difficult. For this example,
we simplify our request to require a specific format to make parsing logic simpler. We can certainly expand on this to
solve these issues, but won’t do that here.

We’ve now completed our simple GRPC example, this has a small protobuf definition, but these can be much more complex,
if needed. We also created a simple docker configuration to get everything running locally and communication between
the service and client.