以前介绍了Google的序列化反序列化工具protobuf。在protobuf的proto文件中除了能够定义message格式,还有一种类型时service。Google想经过service来实现rpc的功能,可是并无在protobuf中实现,而是开放给社区这个接口能够本身实现。同时Google开源了一个官方的实现grpc来生成对应的rpc调用html
首先在proto文件中定义想要的servicejava
syntax = "proto3"; option java_package = "blog.proto"; message Person{ string my_name=1; } message Result{ string string=1; } service HelloService { rpc hello(Person) returns (Result) {} }
官方推荐在grpc中使用proto3,上面能够看到定义了一个HelloService,其下定义了hello方法,Person是入参,Result是出参。须要注意的是入参和出参没法使用简单的数据类型否则会报 Expected message type.服务器
proto文件是须要通过protoc来生成对应的开发语言的源码的,在grpc中须要结合使用grpc的插件来实现proto文件中的service生成java服务端/客户端文件。这里沿用以前的gradle插件ide
protobuf { generatedFilesBaseDir = "$projectDir/src/" plugins { grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.2.0' } } generateProtoTasks { all()*.plugins { grpc {} } } }
在protobuf的配置中加入grpc的插件并,运行generateProto以后就能够在src/main下看到一个新的grpc目录,这个目录中就是生成的service接口,生成的文件在客户端和服务端都须要。注意,只有service的接口/类会生成在这个目录,其余的message定义仍是保持生成在原来的目录。因为grpc目录不是默认的sourceset,因此编译没法找到对应的生成的java文件,不想每次编译都手动增长目录到编译路径,能够在gradle的build文件中将grpc默认加到sourceset中工具
sourceSets { main { java.srcDir 'src/main/grpc' } }
在Server端须要咱们手动重写service的实现并实现Server来启动服务gradle
//服务端的实现继承生成的ImplBase类 public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase { @Override public void hello(blog.proto.ProtoObj.Person request, io.grpc.stub.StreamObserver<blog.proto.ProtoObj.Result> responseObserver) { System.out.println(request.getMyName()+" calling"); //onNext返回值 responseObserver.onNext(ProtoObj.Result.newBuilder().setString("hello, "+request.getMyName()).build()); //服务结束 responseObserver.onCompleted(); } } //这是一个简单的Server实现 public class HelloServer { private int port; private Server server; public HelloServer(int port) throws IOException { this.port=port; //server的builder server=ServerBuilder.forPort(port).addService(new HelloServiceImpl()).build(); //开始服务器 server.start(); System.out.println("Server started, listening on " + port ); } private void blockUntilShutdown() throws InterruptedException { while(true){ server.awaitTermination(); } } public static void main(String[] args) throws Exception { //启动8080端口并block线程 (new HelloServer(8080)).blockUntilShutdown(); } }
以后运行main方法,服务就启动了。ui
Client端在生成完java接口后能够构建Stub与服务器通信this
public class HelloClient { public static void main(String[] args){ //grpc的channel ManagedChannel channel=ManagedChannelBuilder.forAddress("127.0.0.1", 8080).usePlaintext(true).build(); //构建服务的stub HelloServiceGrpc.HelloServiceBlockingStub stub= HelloServiceGrpc.newBlockingStub(channel); ProtoObj.Person person=ProtoObj.Person.newBuilder().setMyName("World").build(); //调用方法 System.out.println(stub.hello(person).getString()); //关闭channel,否则服务端会报错“远程主机强迫关闭了一个现有的链接。” channel.shutdown(); } }
以后运行main方法就能够看到输出hello, World插件