以前介紹了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插件