From Java and REST to GO and gRPC

David Jimenez
Momenton
Published in
8 min readJul 3, 2019
Photo by Austin Ban on Unsplash

How I got to know Go and gRPC coming from a career in backend development of REST with Java.

Most developers tend to know one language/framework and stick to it during their careers.

Some have pet projects in obscure and fun languages but it tends to be more a hobby than a career direction change.

But some have the opportunity to change their core language from time to time.

I have had the opportunity to have worked with three of the mainstream languages of the last 20 years, first ASP.NET, then PHP and finally Java, with a late detour in Javascript (Angular and React and some NodeJS).

But lately I have been hearing more and more about Go and at my current work there is a push to use it as a replacement for Java as a backend language.

So I have dabbled for the last two months with it and these are my impressions.

Go has, in my opinion, four strong points:

  • Platform independence/cross platform
  • Built-in testing and benchmarking facilities
  • Concurrency facilities
  • Simplicity

So coming from a Java background I will discuss each point relative to my experience with Java.

Platform independence

With Go installed it is very easy to create an executable to run in any target platform, the code below will produce a .exe even when run in MacOS or linux:

$env GOOS=windows GOARCH=amd64 go build

This makes deployment extremely easy, and microservice architectures an easy option, for example you can have your macbook pro as a development machine and prepare executables for your clients that use Windows or to deploy to a server that runs Linux.

In Java you can build the .jar (or .class files) and run them but the big drawback is that you need a JRE installed in the target machine.

Testing and benchmarking built-in

This is a nice feature, Go is ready out of the box to make testing and benchmarking very easy, look at the following repos for examples of working code.

The repository above shows a very simple calculator program, the file structure below shows how the test files sit next to each operation:

And below it is a sample of one of those tests:

Notice a couple of things in that file:

  • the package name does not have to match the directory where it resides (package divide_test but the directory is divide)
  • the import for the package tested contains the path from the root of the src in the project
  • the variable t from the testing package

Running tests is quite easy, the most common way is by using the wildcard ./…:

$ go test -v -cover ./…

This will test all files that have the _test in their name and will also show coverage of the unit tests:

Benchmarking:

This other repo shows how easy is to benchmark functions, the example compares concatenation of strings, the equivalent of the famous Java String vs StringBuffer but in Go.

Clone the repo and run the following command:

go test -bench=.

This will run benchmarking of the functions (see the file main_test.go for details). The benchmark results show the following:

It can be seen how nice it is to have the functions and the speed in nanoseconds per operation (the middle column is the number of times it ran the function for a meaningful statistical result, more on this on the official documentation)

In Java testing is quite cumbersome in comparison, since you have to decide which framework to use (JUnit, TestNG) and a package manager plus configuration to set it up, and that is for testing only, for benchmarking the setup and configuration is a whole lot more work.

Concurrency facilities

This is major one for people considering a project in Go, not only concurrency is very easy to implement with goroutines but the memory model of Go allows to share concurrent variables instead of locking, and instead of heavy threads it has a lighter approach that allows less CPU usage. This does not necessarily means that Go is automatically thread safe, but that it is harder to fall into concurrency problems.

Not only that but the `-race` flag which enables checking for possible race conditions when running the tests: https://blog.golang.org/race-detector

Another neat feature of the concurrency model of Go is that each go routine is only about 2KB from the heap memory, compared with 1MB that a normal Java thread uses.

Java in comparison is more cumbersome, however it is not impossible to write concurrent solutions in Java, only there is a lot of thread and/or locks that need to be carefully handled, plus the impact on memory is much higher.

Simplicity

This one could be debatable as a benefit, but regardless of the side to take, Go is a very simple language, which has functions as first class citizens and does not rely in objects and neither has generics, annotations or abstract classes or named interfaces.

It also has implicit interfaces (if the known method is implemented then automatically implements the interface, without any explicit mention of the interface name).

It also allows access to pointers, making it very explicit when there is a reference to a memory location or just a copy of it.

Java is Object oriented (except for primitives) and has a lot of constructs that can contribute to complex and big boilerplate code, this allows the potential for over-engineering and unnecessary design patterns.

In my personal opinion, this aspect of Go was very refreshing and made me like the language a lot.

One of the main design considerations for the Go creators was this simplicity, and as a very interesting comparison this website list the number of keywords among the most popular languages.

Use case for Go: Protocol buffers and gRPC

So with the deep dive and dabbling I have made in Go I found a use case for Go that is very attractive.

Similar to Go, Google has developed their solution to connect services, in fact I found out that they have dumped REST in favour of gRPC for all their communication in internal production, on Google Cloud Platform, and in public-facing APIs.

Why gRPC?

gRPC is a modern open source high performance RPC (Remote Procedure Call) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.(taken from the project’s homepage)

gRPC uses Protocol Buffers to serialise data across the wire, this is instead of XML or JSON.

A key difference between gRPC and REST is the way in which RPC defines its contract negotiation. Whereas REST defines its interactions through terms standardised in its requests, RPC functions upon an idea of contracts, in which the negotiation is defined and constricted by the client-server relationship rather than the architecture itself. RPC gives much of the power (and responsibility) to the client for execution, while offloading much of the handling and computation to the remote server hosting the resource.

The advantages of gRPC over REST, SOAP and even graphQL are immense, some of these are:

  • gRPC is free and open-source framework developed by google
  • gRPC is part of the Cloud Native Computation Foundation (CNCF), like Docker and Kubernetes
  • at high level it allows you to define REQUEST and RESPONSE for RPC and handles the rest for you
  • Built on top of HTTP/2
  • low latency
  • supports streaming
  • language independent
  • easy to plug in authentication, load balancing, logging and monitoring
  • netflix, square and of course google are migrating their internal service communications to gRPC
In gRPC a client application can directly call methods on a server application on a different machine as if it was a local object, making it easier for you to create distributed applications and services.

The following repo demonstrates a simple gRPC server with a client performing calls on a simple remote calculator:

There is some setup that you would need to perform in your computer (explained in the readme) but the basic workflow is as follows:

  • Create your data in a .proto file (in protobuf this is called a message)
message SumRequest {
int32 a = 1;
int32 b = 2;
}
  • Add your services to this file
service CalculatorService{
rpc Sum(SumRequest) returns (SumResponse) {};
}
  • Generate the backing code for the file (getter and setters, utility methods as well as client and server code) in our case in Go (but can be easily in any other mainstream language):
protoc calculatorpb/calculator.proto --go_out=plugins=grpc:.

Then you can start the gRPC server with:

$ go run calculator_server/server.go

which basically looks like this (full code in the repo, reads better there too):

func (*server) Sum(ctx context.Context, req        
*calculatorpb.SumRequest)
(*calculatorpb.SumResponse, error) {
fmt.Printf("Sum function was invoked with %v\n", req)
total := req.GetA() + req.GetB()
res := &calculatorpb.SumResponse {
Result: total,
}
return res, nil
}
func main() {
lis, err := net.Listen("tcp", "0.0.0.0:50051")
.
.
.
s := grpc.NewServer(opts...)
calculatorpb.RegisterCalculatorServiceServer(s, &server{})
}

Notice the function Sum, which the client code calls remotely:

func main() {
fmt.Println("Calculator Client")
cc, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
.
.
.
c := calculatorpb.NewCalculatorServiceClient(cc)
doUnary(c)
}
func doUnary(c calculatorpb.CalculatorServiceClient) {
req := &calculatorpb.SumRequest{
A: 3,
B: 6,
}
res, err := c.Sum(context.Background(), req)
if err != nil {
log.Fatalf("error calling the Sum service %v", err)
}
log.Printf("Result from sum:%d", res.GetResult())
}

This shows a simple unary use case of gRPC, which is the common client server communication similar to REST or the web in general, however gRPC allows these other three types:

  • Client streaming
  • Server streaming
  • Bi-directional streaming

And those are where gRPC is miles ahead, however for the purpose of this article they won’t be described this time.

Photo by Andhika Soreng on Unsplash

In conclusion, Go is a language that was created to satisfy a section of the developers community, in my opinion it sits between Java and C++, its intention is not replace any of them but allow options to professional developers that suits the specific needs of some projects.

--

--