基本概念
GRPC协议是基于protocol buffer作为接口定义以及数据交互的。一个服务端可以支持不同语言的客户端进行访问。
要用protocol buffers序列化数据,首先要定义结构话的proto file,然后可以使用protocol buffers 编译器生成各种语言的类进行编码使用。
// The greeter 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;
}
交互方式定义
一元交互
常规的一问一答,然后结束交互。由服务端完成回复后,进行Completed操作。工作中目前使用该方式,感觉与http请求无异。
rpc SayHello(HelloRequest) returns (HelloResponse);
服务端流式
服务端是话唠,不断的回复数据。由服务端完成回复后,进行Completed操作。有点类似于http chunked的方式。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
客户端流式
客户端是话唠,不断的发数据。在客户端完成所有信息上报后,完成Completed操作。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
双向流式交互
双方各聊各的,可以不断的聊各种话题。这个也有用到,与websocket的交互方式比较类似。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
测试Demo
服务端
package com.demo.grpc.client;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.StreamObserver;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* An example gRPC client that uses ALTS. Shows how to do a Unary RPC. This example can only be run
* on Google Cloud Platform.
*/
public final class HelloWorldAltsClient {
private static final Logger logger = Logger.getLogger(HelloWorldAltsClient.class.getName());
private String serverAddress = "localhost:10001";
public static void main(String[] args) throws InterruptedException {
new HelloWorldAltsClient().runBidiHello();
}
private void runSayHello() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(1);
ManagedChannel channel = ManagedChannelBuilder.forTarget(serverAddress).usePlaintext() //使用纯文本类型
.executor(executor).build();
try {
GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel);
HelloReply resp = stub.sayHello(HelloRequest.newBuilder().setName("Waldo").build());
logger.log(Level.INFO, "Got {0}", resp);
} finally {
channel.shutdown();
channel.awaitTermination(1, TimeUnit.SECONDS);
// Wait until the channel has terminated, since tasks can be queued after the channel is
// shutdown.
executor.shutdown();
}
}
private void runLotsOfGreetings() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(1);
ManagedChannel channel = ManagedChannelBuilder.forTarget(serverAddress).usePlaintext() //使用纯文本类型
.executor(executor).build();
try {
StreamObserver<HelloRequest> helloRequestStreamObserver = GreeterGrpc.newStub(channel).lotsOfGreetings(new StreamObserver<HelloReply>() {
@Override
public void onNext(HelloReply resp) {
logger.log(Level.INFO, "Got {0}", resp);
}
@Override
public void onError(Throwable t) {
logger.log(Level.INFO, "onError");
}
@Override
public void onCompleted() {
logger.log(Level.INFO, "onCompleted");
}
});
helloRequestStreamObserver.onNext(HelloRequest.newBuilder().setName("hello, this is first.").build());
helloRequestStreamObserver.onNext(HelloRequest.newBuilder().setName("hello, this is second.").build());
helloRequestStreamObserver.onCompleted();
} finally {
channel.shutdown();
channel.awaitTermination(1, TimeUnit.SECONDS);
// Wait until the channel has terminated, since tasks can be queued after the channel is
// shutdown.
executor.shutdown();
}
}
private void runBidiHello() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(1);
ManagedChannel channel = ManagedChannelBuilder.forTarget(serverAddress).usePlaintext() //使用纯文本类型
.executor(executor).build();
try {
StreamObserver<HelloRequest> helloRequestStreamObserver = GreeterGrpc.newStub(channel).bidiHello(new StreamObserver<HelloReply>() {
@Override
public void onNext(HelloReply resp) {
logger.log(Level.INFO, "bidiHello Got {0}", resp);
}
@Override
public void onError(Throwable t) {
logger.log(Level.INFO, "onError");
}
@Override
public void onCompleted() {
logger.log(Level.INFO, "onCompleted");
}
});
helloRequestStreamObserver.onNext(HelloRequest.newBuilder().setName("hello, this is first.").build());
helloRequestStreamObserver.onNext(HelloRequest.newBuilder().setName("hello, this is second.").build());
helloRequestStreamObserver.onCompleted();
} finally {
channel.shutdown();
channel.awaitTermination(1, TimeUnit.SECONDS);
// Wait until the channel has terminated, since tasks can be queued after the channel is
// shutdown.
executor.shutdown();
}
}
}
客户端
package com.demo.grpc.server;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.examples.helloworld.GreeterGrpc.GreeterImplBase;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* An example gRPC server that uses ALTS. Shows how to do a Unary RPC. This example can only be run
* on Google Cloud Platform.
*/
public final class HelloWorldAltsServer extends GreeterImplBase {
private static final Logger logger = Logger.getLogger(HelloWorldAltsServer.class.getName());
private Server server;
private int port = 10001;
public static void main(String[] args) throws IOException, InterruptedException {
new HelloWorldAltsServer().start(args);
}
private void start(String[] args) throws IOException, InterruptedException {
server = ServerBuilder.forPort(port)
.addService(this)
.executor(Executors.newFixedThreadPool(1))
.build().start();
logger.log(Level.INFO, "Started on {0}", port);
server.awaitTermination();
}
@Override
public void sayHello(HelloRequest request, StreamObserver<HelloReply> observer) {
observer.onNext(HelloReply.newBuilder().setMessage("Hello, " + request.getName()).build());
observer.onCompleted();
}
@Override
public void lotsOfReplies(HelloRequest request, StreamObserver<HelloReply> observer) {
observer.onNext(HelloReply.newBuilder().setMessage("Hello1, " + request.getName()).build());
observer.onNext(HelloReply.newBuilder().setMessage("Hello2, " + request.getName()).build());
observer.onCompleted();
}
@Override
public StreamObserver<HelloRequest> lotsOfGreetings(StreamObserver<HelloReply> observer) {
return new io.grpc.stub.StreamObserver<HelloRequest>() {
@Override
public void onNext(HelloRequest value) {
logger.log(Level.INFO, "lotsOfGreetings onNext {0}", value);
}
@Override
public void onError(Throwable t) {
logger.info("lotsOfGreetings onError");
}
/**
* 需要注意的是,由client端进行completed, server端被动接收这个信息
*/
@Override
public void onCompleted() {
logger.info("lotsOfGreetings onCompleted");
observer.onNext(HelloReply.newBuilder().setMessage("lotsOfGreetings completed").build());
}
};
}
@Override
public StreamObserver<HelloRequest> bidiHello(StreamObserver<HelloReply> observer) {
return new io.grpc.stub.StreamObserver<HelloRequest>() {
@Override
public void onNext(HelloRequest value) {
logger.log(Level.INFO, "bidiHello onNext {0}", value);
observer.onNext(HelloReply.newBuilder().setMessage("bidiHello onNext1").build());
observer.onNext(HelloReply.newBuilder().setMessage("bidiHello onNext2").build());
}
@Override
public void onError(Throwable t) {
logger.info("bidiHello onError");
t.printStackTrace();
}
/**
* 需要注意的是,由client端进行completed, server端被动接收这个信息
*/
@Override
public void onCompleted() {
logger.info("bidiHello onCompleted");
observer.onNext(HelloReply.newBuilder().setMessage("bidiHello completed1").build());
observer.onNext(HelloReply.newBuilder().setMessage("bidiHello completed2").build());
// observer.onCompleted();
}
};
}
}
proto文件
// 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.
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) {}
rpc LotsOfReplies(HelloRequest) returns (stream HelloReply) {};
rpc LotsOfGreetings(stream HelloRequest) returns (HelloReply) {};
rpc BidiHello(stream HelloRequest) returns (stream 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;
}
踩坑
- 使用grpc协议时,定义proto文件,建议明确定义字段、类型,避免模糊类型,如:map、byte等,失去了结构化的意义,通过文件也无法明确看出协议内容。
- 多个字段时,定义的协议的字段顺序,客户端与服务端必须一致,否则会出现解析错误。