Java:gRPC使用简介

目录

1.开发gRPC应用时,maven需要如何配置?

2.如何生成gRPC java代码?

3.客户端调用方式

4.服务器端业务逻辑运行方式 


以下简单介绍一下如何在java中使用gRPC。

1.开发gRPC应用时,maven需要如何配置?

方式一:


    io.grpc
    grpc-all
    1.26.0

方式二:


  io.grpc
  grpc-netty-shaded
  1.26.0


  io.grpc
  grpc-protobuf
  1.26.0


  io.grpc
  grpc-stub
  1.26.0

方式三:


  io.grpc
  grpc-netty
  1.26.0


  io.grpc
  grpc-protobuf
  1.26.0


  io.grpc
  grpc-stub
  1.26.0

三种方式的区别:
a.
方式一会把与gRPC相关的所有jar都引入,不管实际项目中是否会用到。引入相关jar较多,但其配置简单。
方式三只会引入与gRPC和netty相关的jar,如果要使用OkHttp,则要再引入。配置相对较多,但引入jar相对较少。
方式二引入的jar的数量介于方式一与方式三之间。

b.
方式一中netty是以原始jar引用的。这样方便在使用的过程中查看netty源代码,方便开发。
方式二中netty被放入grpc-netty-shaded-1.26.0.jar的io.grpc.netty.shaded包中。这样的好处是当项目中有多个模块使用了不同版本的netty时,gRPC功能不会受到影响。但坏处就是不方便查看netty源代码了。
方式三与方式一相同,但不会引入像OkHttp这样与netty与gRPC不相关的jar。

总之,方式一和方式三都适合用在只需要使用单一netty版本的环境,方式二适合用在多个netty版本共存的环境 。


2.如何生成gRPC java代码?

使用官方提供的例子helloworld.proto,加以说明:

syntax = "proto3";

option java_package = "com.example.tutorial";
option java_outer_classname = "HelloWorld";

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting
  rpc SayHelloAgain (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;
}

方式一、使用maven
maven中的配置:


    
        
            org.xolstice.maven.plugins
            protobuf-maven-plugin
            0.6.1
            
                com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier}
                grpc-java
                io.grpc:protoc-gen-grpc-java:1.26.0:exe:${os.detected.classifier}
                ${project.build.directory}/generated-sources/protobuf
            

            
                
                    
                        compile
                        compile-custom
                    

                

            

        

    

compile:用于生成protobuf类
compile-custom:生成rpc调用类,其中会用到compile过程中生成的protobuf类

在64位window环境下使用的命令:

mvn protobuf:compile -Dos.detected.name=windows -Dos.detected.arch=x86_64 -Dos.detected.classifier=windows-x86_64
mvn protobuf:compile-custom -Dos.detected.name=windows -Dos.detected.arch=x86_64 -Dos.detected.classifier=windows-x86_64

其中需要传入的三个参数:

os.detected.name
os.detected.arch
os.detected.classifier

这些参数的取值,可以从如下文档中找到:
https://github.com/trustin/os-maven-plugin

对于protobuf-maven-plugin需要用到的参数的详细说明,可参考文档:
Maven Protocol Buffers Plugin
https://www.xolstice.org/protobuf-maven-plugin/

对于如何生成gRPC-Java代码的详细说明,可参考:
gRPC-Java - An RPC library and framework
https://github.com/grpc/grpc-java/blob/master/README.md

方式二、使用命令

// 生成protobuf类
protoc.exe --java_out="D:/Temp" --proto_path="D:/Temp"  helloworld.proto
// 生成rpc调用类
protoc.exe --plugin=protoc-gen-grpc-java=D:/Temp/protoc-gen-grpc-java-1.26.0-windows-x86_64.exe --grpc-java_out="D:/Temp" --proto_path="D:/Temp"  helloworld.proto

Protocol Buffers下载地址:
https://github.com/protocolbuffers/protobuf/releases

protoc-gen-grpc-java可执行文件,下载地址:
https://search.maven.org/search?q=a:protoc-gen-grpc-java

gRPC Java代码生成,参考文档:
gRPC Java Codegen Plugin for Protobuf Compiler
https://github.com/grpc/grpc-java/tree/master/compiler

生成的代码结构:

Java:gRPC使用简介_第1张图片

Java:gRPC使用简介_第2张图片

3.客户端调用方式

官方提供的例子,包括客户端代码和服务器端代码,可以在如下地址找到:
https://github.com/grpc/grpc-java/tree/master/examples/src/main/java/io/grpc/examples/helloworld
其中,客户端例子中使用的是同步调用的方式。

gRPC客户端支持3种调用方式:

a.同步调用
b.基于回调的异步调用
c.基于Future的异步调用

以下提供实现了以上3 种调用方式的客户端代码:

package com.example.tutorial;

import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.example.tutorial.HelloWorld.HelloReply;
import com.example.tutorial.HelloWorld.HelloRequest;
import com.google.common.util.concurrent.ListenableFuture;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;

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

    private String host;
    private int port;

    public HelloWorldClients(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public ManagedChannel getChannel() {
        return ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
    }
    public static void main(String[] args) throws Exception {
	HelloWorldClients client = new HelloWorldClients("localhost", 50051);

	String user;

	user = "world0";
	client.doSync(user);

	user = "world1";
	client.doAsync(user);

	user = "world2";
	client.doFuture(user);
    }
}

 a.同步调用

//  a.同步调用
public void doSync(String name) {
	GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(getChannel());

	logger.info("Will try to doSync");
	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());
}

 b.基于回调的异步调用

//  b.基于回调的异步调用
public void doAsync(String name) {
	GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(getChannel());

	logger.info("Will try to doAsync");
	HelloRequest request = HelloRequest.newBuilder().setName(name).build();

	stub.sayHello(request, new StreamObserver() {

		@Override
		public void onNext(HelloReply response) {
			logger.info("Greeting: " + response.getMessage());
		}

		@Override
		public void onError(Throwable t) {
			logger.log(Level.WARNING, "RPC failed: {0}", t);
		}

		@Override
		public void onCompleted() {
		}
	});
}

 c.基于Future的异步调用

//  c.基于Future的异步调用
public void doFuture(String name) {
	GreeterGrpc.GreeterFutureStub futureStub = GreeterGrpc.newFutureStub(getChannel());

	logger.info("Will try to doFuture");
	HelloRequest request = HelloRequest.newBuilder().setName(name).build();
	HelloReply response;
	try {
		ListenableFuture future = futureStub.sayHello(request);
		response = future.get();
	} catch (InterruptedException e) {
		logger.log(Level.SEVERE, "RPC failed: {0}", e);
		return;
	} catch (ExecutionException e) {
		logger.log(Level.SEVERE, "RPC failed: {0}", e);
		return;
	}
	logger.info("Greeting: " + response.getMessage());
}

4.服务器端业务逻辑运行方式 

业务逻辑运行方式主要分2种:

a.在单独线程池中运行
b.在io-worker线程池中运行

gRPC默认是在单独线程池中运行的:

Java:gRPC使用简介_第3张图片

默认线程池中线程的名字为:grpc-default-executor-X,使用如下代码就会使用默认线程池,默认线程池为CachedThreadPool:

int port = 50051;
Server server = ServerBuilder.forPort(port).addService(new GreeterImpl()).build().start();

也可以设置自己的线程池来运行业务逻辑:

String NAME = "grpc-worker-executor";
Executor executor =   Executors.newFixedThreadPool(5, GrpcUtil.getThreadFactory(NAME + "-%d", true)) ;
int port = 50051;
Server server = ServerBuilder.forPort(port).addService(new GreeterImpl()).executor(executor).build().start();

有时,业务代码运行速度很快,不存在阻塞,可以让其在io-worker线程池中运行:

int port = 50051;
Server server = ServerBuilder.forPort(port).addService(new GreeterImpl()).directExecutor().build().start();

此时,就不会有grpc-default-executor-X出现:

Java:gRPC使用简介_第4张图片

 

参考文档

google开发者中国区网站:
https://developers.google.cn
protocol-buffers官方网站:
https://developers.google.cn/protocol-buffers
protobuf java代码生成方式:
https://developers.google.cn/protocol-buffers/docs/reference/java-generated
Protocol Buffers下载地址:
https://github.com/protocolbuffers/protobuf/releases

你可能感兴趣的:(Java,gRPC)