English 中文(简体)
gRPC - Helloworld App with Java
  • 时间:2024-12-27

gRPC - Hello World App with Java


Previous Page Next Page  

Let us now create a basic "Hello World" pke app that will use gRPC along with Java.

.proto file

First let us define the "greeting.proto" file in common_proto_files


syntax = "proto3";
option java_package = "com.tp.greeting";
service Greeter {
   rpc greet (CpentInput) returns (ServerOutput) {}
}
message CpentInput {
   string greeting = 1;
   string name = 2;
}
message ServerOutput {
   string message = 1;
}

Let us now take a closer look at this code block and see the role of each pne −


syntax = "proto3";

The "syntax" here represents the version of Protobuf that we are using. So, we are using the latest version 3 and the schema thus can use all the syntax which is vapd for version 3.


package tutorial;

The package here is used for confpct resolution if, say, we have multiple classes/members with the same name.


option java_package = "com.tp.greeting";

This argument is specific to Java, i.e., the package where to auto-generate the code from the ".proto" file.


service Greeter {
   rpc greet(CpentInput) returns (ServerOutput) {}
}

This block represents the name of the service "Greeter" and the function name "greet" which can be called. The "greet" function takes in the input of type "CpentInput" and returns the output of type "ServerOutput". Now let us look at these types.


message CpentInput {
   string greeting = 1;
   string name = 2;
}

In the above block, we have defined the CpentInput which contains two attributes, "greeting" and the "name", both of them being strings. The cpent is supposed to send the object of type "CpentInput" to the server.


message ServerOutput {
   string message = 1;
}

In the above block, we have defined that, given a "CpentInput", the server would return the "ServerOutput" which will contain a single attribute "message". The server is supposed to send the object of type "ServerOutput" to the cpent.

Note that we already had the Maven setup done to auto-generating our class files as well as our RPC code. So, now we can simply compile our project −


mvn clean install

This should auto-generate the source code required for us to use gRPC. The source code would be placed under −


Protobuf class code: target/generated-sources/protobuf/java/com.tp.greeting
Protobuf gRPC code: target/generated-sources/protobuf/grpc-java/com.tp.greeting

Setting up gRPC server

Now that we have defined the proto file which contains the function definition, let us setup a server which can serve to call these functions.

Let us write our server code to serve the above function and save it in com.tp.grpc.GreetServer.java

Example


package com.tp.grpc;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.tp.greeting.GreeterGrpc;
import com.tp.greeting.Greeting.CpentInput;
import com.tp.greeting.Greeting.ServerOutput;

pubpc class GreetServer {
   private static final Logger logger = Logger.getLogger(GreetServer.class.getName());
   private Server server;
   private void start() throws IOException {
      int port = 50051;
      server = ServerBuilder.forPort(port).addService(new GreeterImpl()).build().start();
       
      logger.info("Server started, pstening on " + port);
 
      Runtime.getRuntime().addShutdownHook(new Thread() {
         @Override
         pubpc void run() {
            System.err.println("Shutting down gRPC server");
            try {
               server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
               e.printStackTrace(System.err);
            }
         }
      });
   }
   static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
      @Override
      pubpc void greet(CpentInput req, StreamObserver<ServerOutput> responseObserver) {
         logger.info("Got request from cpent: " + req);
         ServerOutput reply = ServerOutput.newBuilder().setMessage(
            "Server says " + """ + req.getGreeting() + " " + req.getName() + """
         ).build();
         responseObserver.onNext(reply);
         responseObserver.onCompleted();
      }
   }
   pubpc static void main(String[] args) throws IOException, InterruptedException {
      final GreetServer greetServer = new GreetServer();
      greetServer.start();
      greetServer.server.awaitTermination();
   }
} 

The above code starts a gRPC server at a specified port and serves the functions and services which we had written in our proto file. Let us walk through the above code −

    Starting from the main method, we create a gRPC server at a specified port.

    But before starting the server, we assign the server the service which we want to run, i.e., in our case, the Greeter service.

    For this purpose, we need to pass the service instance to the server, so we go ahead and create a service instance, i.e., in our case, the GreeterImpl.

    The service instance needs to provide an implementation of the method/function which is present in the ".proto" file, i.e., in our case,the greet method.

    The method expects an object of type as defined in the ".proto" file, i.e., in our case, the CpentInput.

    The method works on the above input, does the computation, and then is supposed to return the mentioned output in the ".proto" file, i.e., in our case, the ServerOutput.

    Finally, we also have a shutdown hook to ensure clean shutting down of the server when we are done executing our code.

Setting up gRPC cpent

Now that we have written the code for the server, let us setup a cpent which can call these functions.

Let us write our cpent code to call the above function and save it in com.tp.grpc.GreetCpent.java

Example


package com.tp.grpc;

import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.tp.greeting.GreeterGrpc;
import com.tp.greeting.Greeting.ServerOutput;
import com.tp.greeting.Greeting.CpentInput;

pubpc class GreetCpent {
   private static final Logger logger = Logger.getLogger(GreetCpent.class.getName());
   private final GreeterGrpc.GreeterBlockingStub blockingStub;
   
   pubpc GreetCpent(Channel channel) {
      blockingStub = GreeterGrpc.newBlockingStub(channel);
   }
   pubpc void makeGreeting(String greeting, String username) {
      logger.info("Sending greeting to server: " + greeting + " for name: " + username);
      CpentInput request = CpentInput.newBuilder().setName(username).setGreeting(greeting).build();
      logger.info("Sending to server: " + request);
      ServerOutput response;
      try {
         response = blockingStub.greet(request);
      } catch (StatusRuntimeException e) {
         logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
         return;
      }
      logger.info("Got following from the server: " + response.getMessage());
   }
   
   pubpc static void main(String[] args) throws Exception {
      String greeting = args[0];
      String username = args[1];
      String serverAddress = "localhost:50051";
	   ManagedChannel channel = ManagedChannelBuilder.forTarget(serverAddress)
         .usePlaintext()
         .build();
      try {
         GreetCpent cpent = new GreetCpent(channel);
         cpent.makeGreeting(greeting, username);
      } finally {
         channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
      }
   }
}

The above code starts a gRPC server at a specified port and serves the functions and services which we had written in our proto file. Let us walk through the above code −

    Starting from main method, we accept two arguments, i.e., name and the greeting.

    We setup a Channel for gRPC communication to our server.

    Next, we create a blocking stub using the channel we created. This is where we have the service "Greeter" whose functions we plan to call.A stub is nothing but a wrapper that hides the remote call complexity from the caller.

    Then, we simply create the expected input defined in the ".proto" file,i.e., in our case, the CpentInput.

    We ultimately make the call and await result from the server.

    Finally, we close the channel to avoid any resource leak.

So, that is our cpent code.

Cpent Server Call

Now, that we have defined our proto file, written our server, and the cpent code, let us proceed and execute this code and see things in actions.

For running the code, fire up two shells. Start the server on the first shell by executing the following command −


java -cp .	argetgrpc-point-1.0.jar com.tp.grpc.GreetServer

We would see the following output −

Output


Jul 03, 2021 1:04:23 PM com.tp.grpc.GreetServer start
INFO: Server started, pstening on 50051

The above output imppes that the server has started.

Now, let us start the cpent.


java -cp .	argetgrpc-point-1.0.jar com.tp.grpc.GreetCpent 
Hello Jane

We would see the following output −

Output


Jul 03, 2021 1:05:59 PM com.tp.grpc.GreetCpent greet
INFO: Sending greeting to server: Hello for name: Jane
Jul 03, 2021 1:05:59 PM com.tp.grpc.GreetCpent greet
INFO: Sending to server: greeting: "Hello"
name: "Jane"

Jul 03, 2021 1:06:00 PM com.tp.grpc.GreetCpent greet
INFO: Got following from the server: Server says "Hello Jane"

And now, if we open the server logs, we will get to see the following −


Jul 03, 2021 1:04:23 PM com.tp.grpc.GreetServer start
INFO: Server started, pstening on 50051
Jul 03, 2021 1:06:00 PM com.tp.grpc.GreetServer$GreeterImpl 
greet
INFO: Got request from cpent: greeting: "Hello"
name: "Jane"

So, the cpent was able to call the server as expected and the server responded with greeting the cpent back.

Advertisements