grpc-java入門實例

本文主要介紹如何在Intellij IDEA環境下,快速搭建出grpc-java的入門實例。這個入門實例是一個HelloWorld程序,客戶端發送world到服務端,服務端接收到請求後,拼裝成 Hello,world返回。

環境配置
JDK1.8
Maven 3.6
Intellij IDEA

創建一個Project

使用Maven自帶的maven-archetype-webapp原型創建一個webapp項目,在src/main下新建對應的java和proto文件夾,並將java文件夾轉換爲 Source Root,項目結構如下圖所示:
grpc-java demo

pom文件中引入相關依賴和插件

	<dependency>
		<groupId>io.grpc</groupId>
		<artifactId>grpc-netty-shaded</artifactId>
    	<version>1.27.2</version>
	</dependency>
	<dependency>
    	<groupId>io.grpc</groupId>
    	<artifactId>grpc-protobuf</artifactId>
    	<version>1.27.2</version>
	</dependency>
	<dependency>
    	<groupId>io.grpc</groupId>
        <artifactId>grpc-stub</artifactId>
        <version>1.27.2</version>
	</dependency>
	<build>
        <finalName>grpc-java</finalName>
        <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.11.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.27.2:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

編寫.proto文件,定義服務接口

在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;
}

通過protocol buffer編譯器生成服務端和客戶端代碼

選中Project,執行Maven Compile操作,會在target/generate-sources下生成對應的代碼。
grpc-java proto buffer

使用Java gRPC API爲上面定義的服務接口編寫客戶端和服務端

在src/main/java文件夾下新建,io.grpc.examples.helloworldPackage,並將代碼放在此文件夾下,與helloworld.proto文件中指定的java_package參數對應。(如果放在其它文件夾下,代碼要做適當調整,因爲代碼的引用位置變了)。

客戶端代碼
HelloWorldClient.java

public class HelloWorldClient {
  private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());

  private final ManagedChannel channel;
  private final GreeterGrpc.GreeterBlockingStub blockingStub;

  /** Construct client connecting to HelloWorld server at {@code host:port}. */
  public HelloWorldClient(String host, int port) {
    this(ManagedChannelBuilder.forAddress(host, port)
        // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
        // needing certificates.
        .usePlaintext()
        .build());
  }

  /** Construct client for accessing HelloWorld server using the existing channel. */
  HelloWorldClient(ManagedChannel channel) {
    this.channel = channel;
    blockingStub = GreeterGrpc.newBlockingStub(channel);
  }

  public void shutdown() throws InterruptedException {
    channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
  }

  /** Say hello to server. */
  public void greet(String name) {
    logger.info("Will try to greet " + name + " ...");
    HelloRequest request = HelloRequest.newBuilder().setName(name).build();
    HelloReply response;
    try {
      response = blockingStub.sayHello(request);
    } catch (StatusRuntimeException e) {
      logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
      return;
    }
    logger.info("Greeting: " + response.getMessage());
  }

  /**
   * Greet server. If provided, the first element of {@code args} is the name to use in the
   * greeting.
   */
  public static void main(String[] args) throws Exception {
    // Access a service running on the local machine on port 50051
    HelloWorldClient client = new HelloWorldClient("localhost", 50051);
    try {
      String user = "world";
      // Use the arg as the name to greet if provided
      if (args.length > 0) {
        user = args[0];
      }
      client.greet(user);
    } finally {
      client.shutdown();
    }
  }
}

服務端代碼
HelloWorldServer.java

public class HelloWorldServer {
  private static final Logger logger = Logger.getLogger(HelloWorldServer.class.getName());

  private Server server;

  private void start() throws IOException {
    /* The port on which the server should run */
    int port = 50051;
    server = ServerBuilder.forPort(port)
        .addService(new GreeterImpl())
        .build()
        .start();
    logger.info("Server started, listening on " + port);
    Runtime.getRuntime().addShutdownHook(new Thread() {
      @Override
      public void run() {
        // Use stderr here since the logger may have been reset by its JVM shutdown hook.
        System.err.println("*** shutting down gRPC server since JVM is shutting down");
        try {
          HelloWorldServer.this.stop();
        } catch (InterruptedException e) {
          e.printStackTrace(System.err);
        }
        System.err.println("*** server shut down");
      }
    });
  }

  private void stop() throws InterruptedException {
    if (server != null) {
      server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
    }
  }

  /**
   * Await termination on the main thread since the grpc library uses daemon threads.
   */
  private void blockUntilShutdown() throws InterruptedException {
    if (server != null) {
      server.awaitTermination();
    }
  }

  /**
   * Main launches the server from the command line.
   */
  public static void main(String[] args) throws IOException, InterruptedException {
    final HelloWorldServer server = new HelloWorldServer();
    server.start();
    server.blockUntilShutdown();
  }

  static class GreeterImpl extends GreeterGrpc.GreeterImplBase {

    @Override
    public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
      HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
      responseObserver.onNext(reply);
      responseObserver.onCompleted();
    }
  }
}

執行代碼

先運行HelloWorldServer,再運行HelloWorldClient,結果如下:
HelloWorldServer
HelloWorldClient
有興趣的話可以改改這個示例,然後看看運行效果。

注意:
如果想在Eclipse環境運行該實例,需要注意以下幾個不同點:

  1. pom文件中插件引用
    Intellij中:
	<extensions>
    	<extension>
    		<groupId>kr.motd.maven</groupId>
    		<artifactId>os-maven-plugin</artifactId>
    		<version>1.6.2</version>
     	</extension>
     </extensions>

eclipse中:

	<plugin>
		<groupId>kr.motd.maven</groupId>
		<artifactId>os-maven-plugin</artifactId>
		<version>1.6.1</version>
		<executions>
			<execution>
			<phase>initialize</phase>
			<goals>
				<goal>detect</goal>
			</goals>
		</execution>
		</executions>
	</plugin>
  1. .proto文件經過IDE編譯後生成的代碼引用問題
    Intellij中可以直接引用target目錄下生成的protobuf代碼,eclipse下不行,需要將生成的文件copy到src/main/java對應的文件夾下(和.proto中配置的java_package參數一致)。另外,因爲jdk編譯版本的問題,文件copy過後,需要去掉一些@java.lang.Override,否則代碼報錯(網上查閱資料,jdk高與1.6版本後就會有這個問題,具體沒有測試過)。

參考資料
gRPC Quick Start
grpc-java源碼地址

------------本文結束感謝您的閱讀------------
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章