Prerequisite
Uses Java 17 with Maven 3, and ensure they are installed and in the path. For example in mac, the ~/.bash_profile should have:
|
1 2 3 4 5 6 7 8 |
... export JAVA_HOME="$(/usr/libexec/java_home -v 17)" export M3_HOME=/Users/ak/tools/apache-maven-3.9.4 ... export PATH=$SPARK_HOME/bin:$JAVA_HOME/bin:$M3_HOME/bin:${PATH} ..... |
and source it.
1. Create an empty Maven project
|
1 2 |
C:\Temp\Java\projects>mvn archetype:generate -DgroupId=com.mytutorial -DartifactId=simple-grpc |
and import or open into an IDE of your choice. I am using IntelliJ IDE.
2. Update the pom.xml file
grpc-netty-shaded – gRPC comes with multiple Transport implementations. The Netty-based HTTP/2 transport is the main transport implementation based on Netty, which is an NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients.
grpc-protobuf – a client-server communication language. Protocol buffers are an Interface Definition Language (i.e. IDL) and serialization library. gRPC uses the IDL.
grpc-stub – client implementation. A stub in distributed computing is a piece of code that converts parameters passed between client and server during a remote procedure call (RPC).
protoc-jar-maven-plugin is a simple maven plugin to compile .proto files using protoc-jar embedded protoc compiler, providing some portability across the major platforms (e.g. Linux, Mac/OSX, and Windows). This plugin runs at build time by detecting the platform and executes the corresponding protoc binary.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mytutorial</groupId> <artifactId>simple-grpc</artifactId> <version>1.0-SNAPSHOT</version> <name>simple-grpc</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.release>17</maven.compiler.release> <protobuf.version>3.6.1</protobuf.version> <grpc.version>1.58.0</grpc.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>5.11.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> <!-- Optionally: parameterized tests support --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>${protobuf.version}</version> </dependency> <!-- gRPC dependencies --> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>${grpc.version}</version> </dependency> <!-- gRPC use this kind of annotation, but not import them directly --> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.4.0</version> </plugin> <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.3.1</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.3.0</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.4.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>3.1.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>3.1.2</version> </plugin> <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.12.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.6.1</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>com.github.os72</groupId> <artifactId>protoc-jar-maven-plugin</artifactId> <version>3.11.4</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>run</goal> </goals> <configuration> <protocArtifact>com.google.protobuf:protoc:3.0.0</protocArtifact> <inputDirectories> <include>src/main/resources</include> </inputDirectories> <outputTargets> <outputTarget> <type>java</type> </outputTarget> <outputTarget> <type>grpc-java</type> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.0.1</pluginArtifact> </outputTarget> </outputTargets> </configuration> </execution> </executions> </plugin> </plugins> </build> </project> |
3. Define the .proto IDL file under src/main/resources
A very simple greet.proto file.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
syntax = "proto3"; option java_package = "com.mytutorial"; service GreetService { rpc SayHello (HelloRequest) returns (HelloResponse) {} } message HelloRequest { string name = 1; /* 1 is a sequence number */ } message HelloResponse { string message = 1; } |
4. run “mvn clean install”
to generate gRPC code that you can see under target/generated-sources
|
1 2 3 |
~/projects/simple-grpc]$ mvn clean install |
5. Add generated-sources to IDE classpath
In IntelliJ:
1) Right click project folder.
2) Select Maven
3) Select Generate Sources And Update Folders
6. GreetServiceImpl using generated-sources
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.mytutorial; import io.grpc.stub.StreamObserver; public class GreetServiceImpl extends GreetServiceGrpc.GreetServiceImplBase { @Override public void sayHello(Greet.HelloRequest request, StreamObserver<Greet.HelloResponse> responseObserver) { Greet.HelloResponse reply = Greet.HelloResponse.newBuilder().setMessage("Hello " + request.getName()).build(); responseObserver.onNext(reply); /* We use the response observer's onCompleted method to specify that we've finished dealing with the RPC */ responseObserver.onCompleted(); } } |
7. ServerMain class
This is the server that accepts gRPC requests.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.mytutorial; import io.grpc.Server; import io.grpc.ServerBuilder; import java.io.IOException; public class ServerMain { public static void main(String[] args) { try { Server server = ServerBuilder.forPort(8999).addService(new GreetServiceImpl()).build(); server.start(); System.out.println("Server started at " + server.getPort()); server.awaitTermination(); } catch (IOException e) { System.out.println("Error: " + e); } catch (InterruptedException e) { System.out.println("Error: " + e); } } } |
8. ClientMain class
This is the client that makes gRPC requests.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.mytutorial; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; public class ClientMain { public static void main(String[] args) { ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8999).usePlaintext().build(); GreetServiceGrpc.GreetServiceBlockingStub bookStub = GreetServiceGrpc.newBlockingStub(channel); Greet.HelloResponse reply = bookStub.sayHello(Greet.HelloRequest.newBuilder().setName("gRPC").build()); System.out.println(reply.getMessage()); channel.shutdown(); } } |
9. Run ServerMain and ClientMain
ServerMain:
|
1 2 3 |
Server started at 8999 |
ClientMain:
|
1 2 3 |
Hello gRPC |
Some gRPC basics
Channel: Channel is an abstraction over a long-lived connection. A gRPC channel provides a connection to a gRPC server on a given host and port. Channels are used to create client stubs.
The client application will create a channel on start up. The channel can be reused/shared among multiple threads. It is thread safe. One channel can be used for connecting to multiple services running on the same gRPC server.
Client stub: gRPC supports two types of client stubs. Blocking/synchronous stubs and asynchronous stubs. newBlockingStub() is used to make blocking calls while newStub() is used for non blocking calls.
In gRPC, stubs and skeletons are code components that serve as intermediaries between the client and the server. They are automatically generated based on the service definitions written in IDL. Stubs are used on the client side, whereas the skeletons, often referred to as service implementations, are used on the server side.
StreamObserver: Service implementations and clients use StreamObservers with onNext(), onError() and onCompleted() methods to receive and publish message using gRPC framework.

