test
前言
网上有很多,尽可能的详细写
官方翻译文档: doc.oschina.net/grpc?t=5683…
官方文档: www.grpc.io/docs/langua…
本文讲述如何创建并使用gRPC(java)
环境
- jdk1.8(要求jdk7及以上)
- maven3.5
实测 jdk6 高版本也可以
如何运行在jdk1.6环境下
grpc version 1.14.0
protobuf.version 3.3.0
新建一个proto文件
在src/main下新建proto文件夹
新建helloworld.proto
内容如下:
syntax = "proto3";
option java_package = "com.meizi.demo.grpc.examples";
package helloworld;
// The greeter service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc BiTalk(stream StreamRequest) returns (stream StreamResponse) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
message StreamRequest {
string request_info = 1;
}
message StreamResponse {
string response_info = 1;
}
关键词解释:
option java_package 指定我们java代码生成的包名,其他语言该参数无效
service 定义一个服务
rpc 定义一个rpc方法
stream 定义该类型为流类型
message 定义请求和响应的类型
这里我们在service里定义了2个rpc方法SayHello和BiTalk,分别对应下文的简单rpc和双向流rpc
使用.proto文件生成java代码
maven的pom文件添加如下插件和依赖
<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.19.1:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.43.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.42.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.42.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-okhttp</artifactId>
<version>1.42.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.42.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.42.1</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.19.1</version>
</dependency>
</dependencies>
执行 maven compile
[INFO] --- protobuf-maven-plugin:0.6.1:compile (default) @ test ---
[INFO] Compiling 1 proto file(s) to /Users/hlw/idea-workspace/test/target/generated-sources/protobuf/java
[INFO]
[INFO] --- protobuf-maven-plugin:0.6.1:compile-custom (default) @ test ---
[INFO] Compiling 1 proto file(s) to /Users/hlw/idea-workspace/test/target/generated-sources/protobuf/grpc-java
生成2个文件,生成的文件提供给下面的客户端和服务端使用
编写服务端代码
package com.****.demo.grpc.examples;
/*
* Copyright 2015 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
/**
* Server that manages startup/shutdown of a {@code Greeter} server.
*/
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();
}
/**
* 覆盖GreeterImplBase的方法,否则客户端调用会报未实现的方法
*/
static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(Helloworld.HelloRequest req, StreamObserver<Helloworld.HelloReply> responseObserver) {
Helloworld.HelloReply reply = Helloworld.HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
// 返回响应
responseObserver.onNext(reply);
// 表明已经完成处理
responseObserver.onCompleted();
}
@Override
public StreamObserver<Helloworld.StreamRequest> biTalk(StreamObserver<Helloworld.StreamResponse> responseObserver) {
return new StreamObserver<Helloworld.StreamRequest>() {
@Override
public void onNext(Helloworld.StreamRequest streamRequest) {
System.out.println("server receive: "+streamRequest.getRequestInfo());
responseObserver.onNext(Helloworld.StreamResponse.newBuilder().setResponseInfo(UUID.randomUUID().toString()).build());
}
@Override
public void onError(Throwable throwable) {
System.out.println(throwable.getMessage());
}
@Override
public void onCompleted() {
responseObserver.onCompleted();
}
};
}
}
}
不要觉得上面代码乱,主要做了几件事
- 创建服务,停止服务的方法
- 创建一个类
GreeterImpl来实现服务定义的方法
编写客户端代码
package com.****.demo.grpc.examples;
/*
* Copyright 2015 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A simple client that requests a greeting from the {@link HelloWorldServer}.
*/
public class HelloWorldClient {
private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());
/**
* 阻塞的存根
*/
private final GreeterGrpc.GreeterBlockingStub blockingStub;
/**
* 异步存根
*/
private final GreeterGrpc.GreeterStub stub;
StreamObserver<Helloworld.StreamRequest> requestStreamObserver;
/** Construct client for accessing HelloWorld server using the existing channel. */
public HelloWorldClient(Channel channel) {
// 'channel' here is a Channel, not a ManagedChannel, so it is not this code's responsibility to
// shut it down.
// Passing Channels to code makes code easier to test and makes it easier to reuse Channels.
blockingStub = GreeterGrpc.newBlockingStub(channel);
stub = GreeterGrpc.newStub(channel);
}
/** Say hello to server. */
public void greet(String name) {
logger.info("Will try to greet " + name + " ...");
Helloworld.HelloRequest request = Helloworld.HelloRequest.newBuilder().setName(name).build();
Helloworld.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());
}
private void biTalk(){
System.out.println("biTalk start");
requestStreamObserver = stub.biTalk(new StreamObserver<Helloworld.StreamResponse>() {
@Override
public void onNext(Helloworld.StreamResponse streamResponse) {
System.out.println("receive: "+streamResponse.getResponseInfo());
}
@Override
public void onError(Throwable throwable) {
System.out.println(throwable.getMessage());
}
@Override
public void onCompleted() {
System.out.println("my onComplated");
}
});
System.out.println("biTalk end");
}
private void sendMsg(String s){
System.out.println("send:" + s);
requestStreamObserver.onNext(Helloworld.StreamRequest.newBuilder().setRequestInfo(s).build());
//requestStreamObserver.onCompleted();
}
/**
* Greet server. If provided, the first element of {@code args} is the name to use in the
* greeting. The second argument is the target server.
*/
public static void main(String[] args) throws Exception {
String user = "world";
// Access a service running on the local machine on port 50051
String target = "localhost:50051";
// Allow passing in the user and target strings as command line arguments
if (args.length > 0) {
if ("--help".equals(args[0])) {
System.err.println("Usage: [name [target]]");
System.err.println("");
System.err.println(" name The name you wish to be greeted by. Defaults to " + user);
System.err.println(" target The server to connect to. Defaults to " + target);
System.exit(1);
}
user = args[0];
}
if (args.length > 1) {
target = args[1];
}
// Create a communication channel to the server, known as a Channel. Channels are thread-safe
// and reusable. It is common to create channels at the beginning of your application and reuse
// them until the application shuts down.
ManagedChannel channel = ManagedChannelBuilder.forTarget(target)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext()
.build();
try {
System.out.println("-----------------------------");
System.out.println("请求-响应,调用SayHello");
HelloWorldClient client = new HelloWorldClient(channel);
client.greet(user);
System.out.println("-----------------------------");
System.out.println("流式请求-流式响应,调用BiTalk");
client.biTalk();
for(int i = 0; i < 10; i++){
client.sendMsg(String.valueOf(i));
Thread.sleep(1000);
}
Thread.sleep(50000);
} finally {
// ManagedChannels use resources like threads and TCP connections. To prevent leaking these
// resources the channel should be shut down when it will no longer be used. If it may be used
// again leave it running.
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
}
}
}
4种不同类型的方法
简单RPC
客户端向服务端发送一个请求,就像调用普通方法一样
rpc SayHello (HelloRequest) returns (HelloReply) {}
服务端流RPC
暂未实现
客户端流RPC
暂未实现
双向流RPC
双方使用读写流发送一系列消息。 这两个流独立运行,因此客户端和服务器可以按照他们喜欢的任何顺序进行读写:例如,服务器可以在写入响应之前等待接收所有客户端消息,或者它可以交替读取消息然后写入消息, 或其他一些读取和写入的组合。 保留每个流中消息的顺序。
rpc BiTalk(stream StreamRequest) returns (stream StreamResponse) {}
问题与解决
grpc相关问题
-
call was half-closed
问题产生:在回调oncompleted后,继续调动onnext
原因:调用过onCompleted(),但现在正在调用onNext();那是不允许的 -
io.grpc.statusruntimeexception:unavaliable:channel shutdownnow invoked
调动ManagedChannel的onclose方法
原因: 可能是调用mchannel.shutdownnow()方法,关闭了通道,但返回调用了oncompleted方法,该方法判断了状态,便抛出这个错
总结
偶碰到grpc相关的使用,故查阅相关文档总结在此。以上是一个初步的使用,更深入的研究挖掘,到官方文档寻找。
参考文献: