gRPC: An Introduction With Scala Compiler

Reading Time: 3 minutes

Hola, does your application have a client-server architecture or microservices architecture or distributed architecture? Then, you must be using some kind of protocol for inter-application communication or you want to get started with one. So, Let’s start digging.

How does inter-application communication takes place?

To not get into networking stuff(TCP, file mapping, etc), you can achieve this with either REST or RPC, there are many frameworks available for RPC, some are, Avro, BERT, Apache Thrift, gRPC, etc. Today, you’ll learn about gRPC.

Introducing gRPC.

Introduction to gRPC

With earlier frameworks there were problems in handling large streamed data. So, key features of gRPC are:

  • It has in-built streaming RPC support.
  • It supports client libraries in 10+ languages.
    – C/C++
    – C#
    – Go
    – Java hence Scala, so on.
  • Back and forth(bi-directional) streaming with HTTP/2
  • Pluggable monitoring, load balancing, tracing, and authorization.

Who introduced gRPC?

Google introduced gRPC(google RPC) back in 2015. So, main motive was to develop a new version of what they were using for their microservices, called Stubby. This new version was supposed to be and is:

  • Free and Open to all
  • Efficient
  • Blocking and non blocking
  • Layered, etc.

If you wish to learn more about its motivation and design, visit this blog.

Working of gRPC

In gRPC, client application can call a method directly which is present on server application on different machine as if it were calling a local object.
gRPC uses protocol buffers(serialization mechanism).

Protocol Buffers:

Working of gRPC with protobuf

According to official documentation, protobufs are:

  • language-neutral
  • platform-neutral
  • extensible mechanism for serializing structured data

The structure of the data is defined only once in .proto file. However, a structure is defined as message. A message has a name and fields represented as name-value pairs. Whereas, a message is small logical unit of information.
On running protobuf compiler, as a result, you will get source code generated. So, you can use this code to read and write your structured data to and from data streams. Generated source code includes:

  • Setters
  • Getters
  • Serializers and Deserializers.
syntax = "proto3";

message Student {
  string name = 1;
  int32 id = 2;
  string email = 3;
}

message Greeting {
    string message = 1;
}

service Greeter {
    rpc SayHello (Student) returns (Greeting) {}
}

If you are using scala, you will have to use ScalaPB and compiler plugin. And, some other dependencies. So, first thing first, let add a plugin to plugins.sbt file.

addSbtPlugin("com.thesamet" % "sbt-protoc" % "0.99.18")

libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.10.1"

Now, lets add the dependencies(for scalaVersion=2.13.1) to build.sbt.

PB.targets in Compile := Seq(
  scalapb.gen() -> (sourceManaged in Compile).value
)

libraryDependencies ++= Seq(
  "io.grpc" % "grpc-netty" % scalapb.compiler.Version.grpcJavaVersion,
  "com.thesamet.scalapb" %% "scalapb-runtime-grpc" % scalapb.compiler.Version.scalapbVersion,
  "com.thesamet.scalapb" %% "scalapb-runtime" % scalapb.compiler.Version.scalapbVersion % "protobuf"
)

Now, lets compile this, I’m using sbt (build tool), you can proceed without one. So, in a terminal run a command

>sbt compile

On successful compilation, you can see in the target/scala2.13/Student folder, we get:

  • Greeting.scala(A final case class)
  • GreeterGrpc.scala (an object having a trait named Greeter)
  • Student.scala (A final case class)

A message will be generated as a final case class(case class has a predefined set of setters and getters and some other set of methods) and service as a trait. So, it will look something like given below:

//Message
@SerialVersionUID(0L)
final case class Student(
    name: _root_.scala.Predef.String = "",
    id: _root_.scala.Int = 0,
    email: _root_.scala.Predef.String = "",
    unknownFields: _root_.scalapb.UnknownFieldSet = _root_.scalapb.UnknownFieldSet.empty
    )

//Service
trait Greeter extends _root_.scalapb.grpc.AbstractService {
    override def serviceCompanion = Greeter
    def sayHello(request: example.Student.Student): scala.concurrent.Future[example.Student.Greeting]
  }


Now, that you have learned about protobufs, so, you can start with the server application. However, it should implement the Greeter trait. Then, give the implementation of the method sayHello(). And also define a client application that will call the method sayHello() defined in a server application.

Thanks for scrolling.
See you then with the new blog on microservices with gRPC and scala.

References:

https://grpc.io/

+ posts