[Java] Updating Java knowledge by writing a gRPC server in Java (1)

4 minute read

I decided to try Java again, so I decided to write a gRPC server to update my knowledge of Java.

This article is wonderful, so I’m just throwing it away, but since a lot of yak shavings are occurring, I would like to include it.

Template for writing a console application with maven

The first chore is to create a very simple Hello World level console application with maven. Simply use maven-archetype-quickstart.

mvn archetype:generate
  -DgroupId=com.simplearchitect \
  -DartifactId=helloGRPC \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DinteractiveMode=false

proto file

Exactly Hello World level protobuf. If you do not set proto3 in syntax, the old version will be used. Since java_package determines the package of the automatically generated class, it may be ok to give it a more serious name. outer_classname is the name of the auto-generated class that represents the concept of protocol.

The protocol buffer below has a method called SayHello and defines a simple service that returns HelloReply when you send HelloRequest.

main/proto/helloworld.proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

You can get a better understanding of the proto file by reading here.

Automatic class generation

It is necessary to automatically generate a class based on the proto file. This requires dependency and plugin to be set in maven. See the top page of gRPC-Java-An RPC library and framework and configure maven.

You need to add the io.grpc type library and include protobuf-maven-plugin and os-maven-plugin. By the way, os-maven-plugin is

os-maven-plugin is a Maven extension/plugin that generates various useful platform-dependent project properties normalized from ${os.name} and ${os.arch}.

That. os-maven-plugin.

<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-netty-shaded</artifactId>
  <version>1.31.1</version>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-protobuf</artifactId>
  <version>1.31.1</version>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-stub</artifactId>
  <version>1.31.1</version>
</dependency>
<dependency> <!-- necessary for Java 9+ -->
  <groupId>org.apache.tomcat</groupId>
  <artifactId>annotations-api</artifactId>
  <version>6.0.53</version>
  <scope>provided</scope>
</dependency>
::
<build>
  <extensions>
    <extension>
      <groupId>kr.motd.maven</groupId>
      <artifactId>os-maven-plugin</artifactId>
      <version>1.6.2</version>
    </extension>
  </extensions>
  <plugins>
    <plugin>
      <groupId>org.xolstice.maven.plugins</groupId>
      <artifactId>protobuf-maven-plugin</artifactId>
      <version>0.6.1</version>
      <configuration>
        <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
        <pluginId>grpc-java</pluginId>
        <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.31.1:exe:${os.detected.classifier}</pluginArtifact>
      </configuration>
      <executions>
        <execution>
          <goals>
            <goal>compile</goal>
            <goal>compile-custom</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

With this setting, the class will be automatically generated by the mvn clean package. image.png

By the way, when creating a project in IntelliJ, it is better to execute it after generating target once. If you keep the default, the target directory will not be displayed. After creating the target, load the project into intelliJ and it will set it automatically. If not, it is necessary to set the following settings File> Project Structure Module.

image.png

In addition, when loading to IntelliJ, if you do not right click the project and select an additional one as a Maven project, even if no error occurs in maven, the dependency cannot be resolved on the screen and it becomes Error. Will end up.

write a gRPC server

Write the server by referencing the generated classes. Allow this to be run from the Main program. It simply starts the server, configures it, and sets the inherited class.

package com.simplearchitect;

import java.io.IOException;
import java.util.logging.Logger;

import io.grpc.BindableService;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.GreeterGrpc.GreeterImplBase;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.CallStreamObserver;

import static io.grpc.ServerBuilder.*;

public class SimpleServer {
    Logger logger = Logger.getLogger(getClass().getName());Server server;

    public void start() throws IOException {
        server = ServerBuilder
                .forPort(8080)
                .addService((BindableService) new SimpleHelloServiceImpl())
                .build()
                .start();
        logger.info("start gRPC server.");
    }

    public void stop() {
        if (server != null) {
            server.shutdown();
            logger.info("Shutting down gRPC server.");
        }
    }

    public void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    public static class SimpleHelloServiceImpl extends GreeterImplBase {
        Logger logger = Logger.getLogger(getClass().getName());

        public void sayHello(HelloRequest request, CallStreamObserver<HelloReply> responseObserver) {
            logger.info(String.format("request: name = %s", request.getName()));
            HelloReply reply = HelloReply.newBuilder().setMessage("Hello, " + request.getName()).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}

abstract static class

不思議なことに、static クラスを new している。これはどういうことだろうか? 下記のクラスが継承元のクラスだが、static abstract class になっている。これは、実際は、static クラスではなく、このクラスは、インナークラスになっており、インナークラスの親のクラスをインスタンス化していなくても、インスタンス化できることを示すため、static がついているので、実態はインスタンス化できるクラスである。

public static abstract class GreeterImplBase implements io.grpc.BindableService {

    /**
     * <pre>
     * Sends a greeting
     * </pre>
     */
    public void sayHello(io.grpc.examples.helloworld.HelloRequest request,
        io.grpc.stub.StreamObserver<io.grpc.examples.helloworld.HelloReply> responseObserver) {
      asyncUnimplementedUnaryCall(getSayHelloMethod(), responseObserver);
    }

    @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
      return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
          .addMethod(
            getSayHelloMethod(),
            asyncUnaryCall(
              new MethodHandlers<
                io.grpc.examples.helloworld.HelloRequest,
                io.grpc.examples.helloworld.HelloReply>(
                  this, METHODID_SAY_HELLO)))
          .build();
    }
  }

Java への Dependency の追加

mvn clean package で jar ファイルを作って実行するとエラーになる。Jar にライブラリが含まれていないためだ。maven-assembly-plugin を追加しよう。これで、mainClass も指定できて、依存関係にある jar を含んだライブラリを作ってくれる。 `

      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
          <archive>
            <manifest>
              <mainClass>com.simplearchitect.App</mainClass>
            </manifest>
          </archive>
        </configuration>
        <executions>
          <execution>
            <id>sample</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

The Assembly Plugin for Maven enables developers to combine project output into a single distributable archive that also contains dependencies, modules, site documentation, and other files.

うむ。まさに求めていたもの。

実行

PS > > java -jar .\target\helloGRPC-1.0-SNAPSHOT-jar-with-dependencies.jar
Aug 16, 2020 11:17:33 PM com.simplearchitect.SimpleServer start
INFO: start gRPC server.
> Enter stop.

ちゃんと動くかしらんけど、まずは、コンパイルされて、ライブラリ付きの jar ができて、動くところまでは出来た。次回はクライアントを作ってみて、動かしてみる。CallStreamObserver の挙動を明確に理解したい。

Tags:

Updated: