Grpc的指南网站https://grpc.io/docs/guides/
指南
本文档向您介绍gRPC和协议缓冲区。 gRPC可以使用协议缓冲区作为其接口定义语言(IDL)和其基础消息交换格式。 如果您是gRPC和/或协议缓冲区的新手,请阅读本文! 如果您只想潜入并首先看到gRPC,请参阅我们的快速入门。
在gRPC中,客户端应用程序可以直接调用不同计算机上的服务器应用程序上的方法,就像它是本地对象一样,使您可以更轻松地创建分布式应用程序和服务。 与许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。 在服务器端,服务器实现此接口并运行gRPC服务器来处理客户端调用。 在客户端,客户端有一个存根(在某些语言中称为客户端),它提供与服务器相同的方法。
gRPC客户端和服务器可以在各种环境中相互运行和通信 - 从Google内部的服务器到您自己的桌面 - 并且可以使用任何gRPC支持的语言编写。因此,例如,您可以使用Go,Python或Ruby轻松创建Java中的gRPC服务器。此外,最新的Google API将具有gRPC版本的界面,让您可以轻松地在应用程序中构建Google功能。
使用协议缓冲区
默认情况下,gRPC使用协议缓冲区,这是Google成熟的开源机制,用于序列化结构化数据(尽管它可以与其他数据格式(如JSON)一起使用)。这是一个如何工作的快速介绍。如果您已经熟悉协议缓冲区,请随时跳到下一部分。
使用协议缓冲区的第一步是定义要在proto文件中序列化的数据的结构:这是一个扩展名为.proto的普通文本文件。协议缓冲区数据被构造为消息,其中每个消息是包含一系列称为字段的名称 - 值对的信息的小逻辑记录。这是一个简单的例子:
message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
然后,一旦指定了数据结构,就可以使用协议缓冲区编译器protoc从原型定义生成首选语言的数据访问类。 这些为每个字段提供了简单的访问器(如name()和set_name()),以及将整个结构序列化/解析为原始字节的方法 - 例如,如果您选择的语言是C ++,则运行编译器 上面的例子将生成一个名为Person的类。 然后,您可以在应用程序中使用此类来填充,序列化和检索Person协议缓冲区消息。
正如您将在我们的示例中更详细地看到的那样,您可以在普通的proto文件中定义gRPC服务,并将RPC方法参数和返回类型指定为协议缓冲区消息:
// 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;
}
gRPC还使用带有特殊gRPC插件的protoc来生成proto文件中的代码。但是,使用gRPC插件,您将获得生成的gRPC客户端和服务器代码,以及用于填充,序列化和检索消息类型的常规协议缓冲区代码。我们将在下面更详细地看一下这个例子。
您可以在Protocol Buffers文档中找到有关协议缓冲区的更多信息,并了解如何使用所选语言的快速入门获取和安装带有gRPC插件的protoc。
协议缓冲版本
虽然协议缓冲区已经可供开源用户使用一段时间,但我们的示例使用了一种新的协议缓冲区,称为proto3,它具有略微简化的语法,一些有用的新功能,并支持更多语言。目前提供Java,C ++,Python,Objective-C,C#,来自协议缓冲区GitHub repo的lite-runtime(Android Java),Ruby和JavaScript,以及来自golang / protobuf GitHub的Go语言生成器repo,开发中有更多语言。您可以在proto3语言指南和每种语言的参考文档中找到更多信息。参考文档还包括.proto文件格式的正式规范。
通常,虽然您可以使用proto2(当前的默认协议缓冲版本),但我们建议您将proto3与gRPC一起使用,因为它允许您使用全系列的gRPC支持的语言,以及避免与proto2客户端通信时的兼容性问题proto3服务器,反之亦然。
gRPC允许您定义四种服务方法:
一元RPC,客户端向服务器发送单个请求并返回单个响应,就像正常的函数调用一样。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
服务器流式RPC,客户端向服务器发送请求并获取流以读取消息序列。 客户端从返回的流中读取,直到没有更多消息。 gRPC保证单个RPC调用中的消息排序。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
客户端流式RPC,客户端再次使用提供的流写入一系列消息并将其发送到服务器。 一旦客户端写完消息,它就等待服务器读取它们并返回它的响应。 gRPC再次保证在单个RPC调用中的消息排序
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
双向流式RPC,双方使用读写流发送一系列消息。 这两个流独立运行,因此客户端和服务器可以按照自己喜欢的顺序进行读写:例如,服务器可以在写入响应之前等待接收所有客户端消息,或者它可以交替地读取消息然后写入消息, 或者其他一些读写组合。 保留每个流中的消息顺序。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
io.netty
netty-all
4.1.36.Final
org.apache.thrift
libthrift
0.12.0
io.grpc
grpc-netty-shaded
1.21.0
io.grpc
grpc-protobuf
1.21.0
io.grpc
grpc-stub
1.21.0
org.slf4j
slf4j-nop
1.7.2
kr.motd.maven
os-maven-plugin
1.5.0.Final
org.apache.maven.plugins
maven-compiler-plugin
1.8
org.xolstice.maven.plugins
protobuf-maven-plugin
0.5.1
com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}
grpc-java
io.grpc:protoc-gen-grpc-java:1.21.0:exe:${os.detected.classifier}
compile
compile-custom
Student.proto
syntax = "proto3";
package com.kkagr.proto;
option java_package = "com.kkagr.proto";
option java_outer_classname = "StudentProto";
option java_multiple_files = true;
service StudentService{
rpc GetRealNameByUsername(MyRequest) returns(MyResponse){}
rpc GetStudentsByAge(StudentRequest) returns(stream StudentResponse){}
rpc GetStudentsWrapperByAges(stream StudentRequest) returns(StudentResponseList){}
rpc BiTalk(stream StreamRequest) returns(stream StreamResponse){}
}
message MyRequest{
string username =1;
}
message MyResponse{
string realname =2;
}
message StudentRequest{
int32 age = 1;
}
message StudentResponse{
string name =1;
int32 age =2;
string city =3;
}
message StudentResponseList{
repeated StudentResponse studentResponse =1;
}
message StreamRequest{
string request_info =1;
}
message StreamResponse{
string response_info =1;
}
使用maven install会生成protobuf对应的文件,然后拷贝上上图目录。
新建StudentServiceImpl
package com.kkagr.grpc;
import com.kkagr.proto.MyRequest;
import com.kkagr.proto.MyResponse;
import com.kkagr.proto.StreamRequest;
import com.kkagr.proto.StreamResponse;
import com.kkagr.proto.StudentRequest;
import com.kkagr.proto.StudentResponse;
import com.kkagr.proto.StudentResponseList;
import com.kkagr.proto.StudentServiceGrpc;
import java.util.UUID;
import io.grpc.stub.StreamObserver;
public class StudentServiceImpl extends StudentServiceGrpc.StudentServiceImplBase {
@Override
public void getRealNameByUsername(MyRequest request, StreamObserver responseObserver) {
System.out.println("接受到客户端消息:"+request.getUsername());
responseObserver.onNext(MyResponse.newBuilder().setRealname("张三").build());
responseObserver.onCompleted();
}
@Override
public void getStudentByAge(StudentRequest request, StreamObserver responseObserver) {
System.out.println("接受到客户端消息"+request.getAge());
responseObserver.onNext(StudentResponse.newBuilder().setName("张三").setAge(20).setCity("北京").build());
responseObserver.onNext(StudentResponse.newBuilder().setName("李四").setAge(20).setCity("成都").build());
responseObserver.onCompleted();
}
@Override
public StreamObserver getStudentsWrapperByAge(StreamObserver responseObserver) {
return new StreamObserver() {
@Override
public void onNext(StudentRequest studentRequest) {
System.out.println("onNext"+studentRequest.getAge());
}
@Override
public void onError(Throwable throwable) {
System.out.println(throwable.getMessage());
}
@Override
public void onCompleted() {
StudentResponse studentResponse = StudentResponse.newBuilder().setName("张三").setAge(20).setCity("北京").build();
StudentResponse studentResponse2 = StudentResponse.newBuilder().setName("李四").setAge(30).setCity("成都").build();
StudentResponseList studentResponseList = StudentResponseList.newBuilder()
.addStudentResponse(studentResponse).addStudentResponse(studentResponse2).build();
responseObserver.onNext(studentResponseList);
responseObserver.onCompleted();
}
};
}
@Override
public StreamObserver bidTalk(StreamObserver responseObserver) {
return new StreamObserver() {
@Override
public void onNext(StreamRequest streamRequest) {
System.out.println(streamRequest.getRequestInfo());
responseObserver.onNext(StreamResponse.newBuilder().setRequestInfo(UUID.randomUUID().toString()).build());
}
@Override
public void onError(Throwable throwable) {
System.out.println(throwable.getMessage());
}
@Override
public void onCompleted() {
responseObserver.onCompleted();
}
};
}
}
GrpcService
package com.kkagr.grpc;
import java.io.IOException;
import io.grpc.Server;
import io.grpc.ServerBuilder;
public class GrpcService {
private Server server;
private void start() throws IOException{
this.server = ServerBuilder.forPort(8899).addService(new StudentServiceImpl()).build().start();
System.out.println("server start");
Runtime.getRuntime().addShutdownHook(new Thread(()->{
System.out.println("关闭jvm");
GrpcService.this.stop();
}));
System.out.println("执行到这里");
}
private void stop(){
if(null != this.server){
this.server.shutdown();
}
}
private void awaitTermination() throws InterruptedException{
if(null != this.server){
this.server.awaitTermination();
}
}
public static void main(String[] args) throws IOException, InterruptedException{
GrpcService service = new GrpcService();
service.start();
service.awaitTermination();
}
}
GrpcClient
package com.kkagr.grpc;
import com.kkagr.proto.MyRequest;
import com.kkagr.proto.MyResponse;
import com.kkagr.proto.StreamRequest;
import com.kkagr.proto.StreamResponse;
import com.kkagr.proto.StudentRequest;
import com.kkagr.proto.StudentResponse;
import com.kkagr.proto.StudentResponseList;
import com.kkagr.proto.StudentServiceGrpc;
import java.time.LocalDateTime;
import java.util.Iterator;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
public class GrpcClient {
public static void main(String[] args) {
ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("127.0.0.1",8899).usePlaintext(true).build();
StudentServiceGrpc.StudentServiceBlockingStub blockingStub = StudentServiceGrpc.newBlockingStub(managedChannel);
StudentServiceGrpc.StudentServiceStub stub = StudentServiceGrpc.newStub(managedChannel);
MyResponse myResponse = blockingStub.getRealNameByUsername(MyRequest.newBuilder().setUsername("zhangsan").build());
System.out.println(myResponse.getRealname());
System.out.println("======================");
/*Iterator iter = blockingStub.getStudentByAge(StudentRequest.newBuilder().setAge(20).build());
while (iter.hasNext()){
StudentResponse studentResponse = iter.next();
System.out.println(studentResponse.getName()+","+studentResponse.getAge()+","+studentResponse.getCity());
}
System.out.println("======================");
StreamObserver requestStreamObserver = stub.bidTalk(new StreamObserver() {
@Override
public void onNext(StreamResponse streamResponse) {
System.out.println(streamResponse.getRequestInfo());
}
@Override
public void onError(Throwable throwable) {
System.out.println(throwable.getMessage());
}
@Override
public void onCompleted() {
System.out.println("completed");
}
});
System.out.println("======================");
for(int i =0;i<10;i++){
requestStreamObserver.onNext(StreamRequest.newBuilder().setRequestInfo(LocalDateTime.now().toString()).build());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(50000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
运行效果,客户端异常关闭,服务端也会抛出异常,这个没有什么影响。
node.js可以使用webstorm去编写,配置很简单,网上百度即可搞定,这里使用的是动态的proto文件。
目录结构
新建package.json
{
"name": "grpc-examples",
"version": "0.1.0",
"dependencies": {
"async": "^1.5.2",
"google-protobuf": "^3.0.0",
"grpc": "^1.0.0",
"lodash": "^4.6.1",
"minimist": "^1.2.0"
}
}
Student.proto和上面的一样
grpcService.js
var PROTO_FILE_PATH="D:\\webstormWorkSpace\\proto\\Student.proto";
var grpc = require('grpc');
var grpcService = grpc.load(PROTO_FILE_PATH).com.kkagr.proto;
var server = new grpc.Server();
server.addService(grpcService.StudentService.service,{
getRealNameByUsername:getRealNameByUsername,
getStudentsByAge:getStudentsByAge,
getStudentsWrapperByAges:getStudentsWrapperByAges,
BiTalk:biTalk
});
server.bind('127.0.0.1:8899',grpc.ServerCredentials.createInsecure());
server.start();
function getRealNameByUsername(call,callback){
console.log("call:"+call);
callback(null,{realname:'张三'});
}
function getStudentsByAge(){
}
function getStudentsWrapperByAges(){
}
function biTalk(){
}
grpcClient.js
var PROTO_FILE_PATH="D:\\webstormWorkSpace\\proto\\Student.proto";
var grpc = require('grpc');
var grpcService = grpc.load(PROTO_FILE_PATH).com.kkagr.proto;
var client = new grpcService.StudentService('127.0.0.1:8899',grpc.credentials.createInsecure());
client.getRealNameByUsername({username:'lisi'},function (error,respData) {
console.log(respData)
});