English 中文(简体)
gRPC - Unary
  • 时间:2024-11-03

gRPC - Unary gRPC


Previous Page Next Page  

We will now look at various types of communication that the gRPC framework supports. We will use an example of Bookstore where the cpent can search and place an order for book depvery.

Let us see unary gRPC communication where we let the cpent search for a title and return randomly one of the book matching the title queried for.

.proto file

First let us define the bookstore.proto file in common_proto_files −


syntax = "proto3";
option java_package = "com.tp.bookstore";

service BookStore {
   rpc first (BookSearch) returns (Book) {}
}
message BookSearch {
   string name = 1;
   string author = 2;
   string genre = 3;
}
message Book {
   string name = 1;
   string author = 2;
   int32 price = 3;
}

Let us now take a closer look at each of the pnes in the above block.


syntax = "proto3";

Syntax

The "syntax" here represents the version of Protobuf we are using. 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.bookstore";

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


service BookStore {
   rpc first (BookSearch) returns (Book) {}
}

This represents the name of the service "BookStore" and the function name "first" which can be called. The "first" function takes in the input of type "BookSearch" and returns the output of type "Book". So, effectively, we let the cpent search for a title and return one of the book matching the title queried for.

Now let us look at these types.


message BookSearch {
   string name = 1;
   string author = 2;
   string genre = 3;
}

In the above block, we have defined the BookSearch which contains the attributes pke name, author and genre. The cpent is supposed to send the object of type of "BookSearch" to the server.


message Book {
   string name = 1;
   string author = 2;
   int32 price = 3;
}

Here, we have also defined that, given a "BookSearch", the server would return the "Book" which contains book attributes along with the price of the book. The server is supposed to send the object of type of "Book" to the cpent.

Note that we already had the Maven setup done for 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.bookstore
Protobuf gRPC code: target/generated-sources/protobuf/grpc-java/com.tp.bookstore

Setting up gRPC Server

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

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

Example


package com.tp.bookstore;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import com.tp.bookstore.BookStoreOuterClass.Book;
import com.tp.bookstore.BookStoreOuterClass.BookSearch;
pubpc class BookeStoreServerUnary {
   private static final Logger logger = Logger.getLogger(BookeStoreServerUnary.class.getName());
 
   static Map<String, Book> bookMap = new HashMap<>();
   static {
      bookMap.put("Great Gatsby", Book.newBuilder().setName("Great Gatsby")
         .setAuthor("Scott Fitzgerald")
         .setPrice(300).build());
      bookMap.put("To Kill MockingBird", Book.newBuilder().setName("To Kill MockingBird")
         .setAuthor("Harper Lee")
         .setPrice(400).build());
      bookMap.put("Passage to India", Book.newBuilder().setName("Passage to India")
         .setAuthor("E.M.Forster")
         .setPrice(500).build());
      bookMap.put("The Side of Paradise", Book.newBuilder().setName("The Side of Paradise")
         .setAuthor("Scott Fitzgerald")
         .setPrice(600).build());
      bookMap.put("Go Set a Watchman", Book.newBuilder().setName("Go Set a Watchman")
         .setAuthor("Harper Lee")
         .setPrice(700).build());
   }
   private Server server;
   private void start() throws IOException {
      int port = 50051;
      server = ServerBuilder.forPort(port)
         .addService(new BookStoreImpl()).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);
            }
         }
      });
   }
   pubpc static void main(String[] args) throws IOException, InterruptedException {
      final BookeStoreServerUnary greetServer = new BookeStoreServerUnary();
      greetServer.start();
      greetServer.server.awaitTermination();
   }
   static class BookStoreImpl extends BookStoreGrpc.BookStoreImplBase {
      @Override
      pubpc void first(BookSearch searchQuery, StreamObserver<Book> responseObserver) {
         logger.info("Searching for book with title: " + searchQuery.getName());
         List<String> matchingBookTitles = bookMap.keySet().stream().filter(title ->
            title.startsWith(searchQuery.getName().trim())).collect(Collectors.toList());

         Book foundBook = null;
         if(matchingBookTitles.size() > 0) {
            foundBook = bookMap.get(matchingBookTitles.get(0));
         }
         responseObserver.onNext(foundBook);
         responseObserver.onCompleted();
      }
   }
}

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 BookStore 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 BookStoreImpl

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

    The method expects an object of type as defined in the .proto file, i.e.,for us the BookSearch

    The method searches for the book in the available bookMap and then returns the Book by calpng the onNext() method. Once done, the server announces that it is done with the output by calpng onCompleted()

    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.bookstore.BookStoreCpentUnaryBlocking.java

Example


package com.tp.bookstore;

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.bookstore.BookStoreOuterClass.Book;
import com.tp.bookstore.BookStoreOuterClass.BookSearch;
import com.tp.greeting.GreeterGrpc;
import com.tp.greeting.Greeting.ServerOutput;
import com.tp.greeting.Greeting.CpentInput;

pubpc class BookStoreCpentUnaryBlocking {
   private static final Logger logger = Logger.getLogger(BookStoreCpentUnaryBlocking.class.getName());
   private final BookStoreGrpc.BookStoreBlockingStub blockingStub;
	
   pubpc BookStoreCpentUnaryBlocking(Channel channel) {
      blockingStub = BookStoreGrpc.newBlockingStub(channel);
   }
   pubpc void getBook(String bookName) {
      logger.info("Querying for book with title: " + bookName);
      BookSearch request = BookSearch.newBuilder().setName(bookName).build();
 
   Book response; 
   try {
      response = blockingStub.first(request);
      } catch (StatusRuntimeException e) {
         logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
         return;
      }
      logger.info("Got following book from server: " + response);
   }
   pubpc static void main(String[] args) throws Exception {
      String bookName = args[0];
      String serverAddress = "localhost:50051";
	  
      ManagedChannel channel = ManagedChannelBuilder.forTarget(serverAddress)
         .usePlaintext()
         .build();
 
      try {
         BookStoreCpentUnaryBlocking cpent = new 
         BookStoreCpentUnaryBlocking(channel);
         cpent.getBook(bookName);
      } 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 the main method, we accept one argument, i.e., the title of the book we want to search for.

    We setup a Channel for gRPC communication with our server.

    And then, we create a blocking stub using the channel. This is where we choose the service "BookStore" whose functions we plan to call. A "stub" is nothing but a wrapper which hides the complexity of the remote call from the caller.

    Then, we simply create the expected input defined in the .proto file,i.e., in our case BookSearch and we add the title name we want the server to search for.

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

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

So, that is our cpent code.

Cpent Server Call

To sum up, what we want to do is the following −

    Start the gRPC server.

    The Cpent queries the Server for a book with a given name/title.

    The Server searches the book in its store.

    The Server then responds with the book and its other attributes.

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

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.bookstore.BookeStoreServerUnary

We would see the following output −

Output


Jul 03, 2021 7:21:58 PM 
com.tp.bookstore.BookeStoreServerUnary start
INFO: Server started, pstening on 50051

The above output means the server has started.

Now, let us start the cpent.


java -cp .	argetgrpc-point-1.0.jar 
com.tp.bookstore.BookStoreCpentUnaryBlocking "To Kill"

We would see the following output −

Output


Jul 03, 2021 7:22:03 PM 
com.tp.bookstore.BookStoreCpentUnaryBlocking getBook
INFO: Querying for book with title: To Kill

Jul 03, 2021 7:22:04 PM 
com.tp.bookstore.BookStoreCpentUnaryBlocking getBook
INFO: Got following book from server: name: "To Kill 

MockingBird"
author: "Harper Lee"
price: 400

So, as we see, the cpent was able to get the book details by querying the server with the name of the book.

Advertisements